{
  "openapi": "3.1.0",
  "info": {
    "title": "Clearvo E-Invoicing API",
    "version": "1.0.0",
    "description": "Single REST API for B2B e-invoicing across 37 countries. Handles format generation (UBL, FatturaPA, XRechnung, VeriFactu, KSeF, etc.), authority submission, clearance polling, and webhooks.\n\n## Rate limits\n\nEvery authenticated request is counted against the API key that made it, in fixed 60-second windows, however many IP addresses the key is used from. Sandbox (`csk_test_`) and production (`csk_live_`) keys are counted separately. Limits are per key, per endpoint class:\n\n| Endpoint class | Endpoints | Limit per key |\n|---|---|---|\n| Bulk submission | `POST /send/bulk`, `POST /receive/bulk` | 30 requests/min |\n| Single submission | `POST /send`, `POST /receive` | 120 requests/min |\n| Tax Calculations | `POST /tax/calculate`, `POST /duties/quote`, `POST /tax/calculate/{id}/refund`, `POST /integrations/coupa/tax-callout` | 600 requests/min |\n| Heavy operations | `GET /pt/saft`, `POST /invoices/resend-notification/batch`, `POST /mx/inbound/cfdi`, `POST /mx/inbound/poll` | 60 requests/min |\n| Everything else | every other authenticated endpoint (reads, status polls, entity and registration management, webhooks configuration, ...) | 300 requests/min |\n\n`POST /query` and `GET /export` have their own per-key budgets, documented on those operations, and are not counted here. `GET /health` is unauthenticated and not counted.\n\n**Bulk rows budget.** On top of the request count, the synchronous `POST /send/bulk` transport (uploads under 25MB) charges the upload's CSV data rows, and `POST /receive/bulk` charges its documents, against a budget of 20,000 rows per minute per key. An upload larger than what remains of that budget in the current window is rejected whole with 429 — nothing in it is processed — so split the file or wait for the window to reset. Uploads of 25MB or more to `POST /send/bulk` (up to 200MB) are stored and processed asynchronously and are not row-budgeted.\n\n**Response headers.** Every counted response, success or error, carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` (Unix epoch seconds at which the window resets) and `X-RateLimit-Scope: api-key`. `POST /send/bulk` (synchronous) and `POST /receive/bulk` additionally carry `X-RateLimit-Rows-Limit`, `X-RateLimit-Rows-Remaining` and `X-RateLimit-Rows-Reset`. A 429 also carries `Retry-After` (seconds).\n\n**429 response.** Every rate-limited request returns the `RateLimited` response: `{ \"ok\": false, \"error\": { \"code\": \"RATE_LIMIT_EXCEEDED\", \"message\": \"...\", \"retryable\": true }, \"hint\": \"...\" }`. The request was not processed. Wait `Retry-After` seconds before retrying, and spread bursts out rather than retrying in a tight loop. A separate network-level protection against abusive traffic also exists, set well above the per-key limits; it returns the same envelope with `X-RateLimit-Scope: ip` and an `X-RateLimit-IP-Remaining` header instead of the per-key headers, and a legitimate integration should never encounter it. If you need higher limits, contact support@clearvo.io.",
    "contact": {
      "name": "Clearvo Support",
      "url": "https://clearvo.io",
      "email": "support@clearvo.io"
    },
    "license": {
      "name": "Proprietary"
    }
  },
  "servers": [
    {
      "url": "https://api.clearvo.io/v1",
      "description": "Production"
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "API key prefixed with `csk_live_` for production or `csk_test_` for sandbox. Create a test key in Sandbox mode in the dashboard (Enterprise plan); live and test keys are separate keys."
      },
      "OAuthBearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "OAuth 2.0 access token issued to a connected app (e.g. the WooCommerce integration). Accepted as an alternative to `x-api-key` on the routes that document it — either credential is sufficient, not both."
      }
    },
    "headers": {
      "X-RateLimit-Limit": {
        "schema": {
          "type": "integer"
        },
        "description": "Requests allowed per 60-second window for this API key on this endpoint's class — see the Rate limits section of the API overview."
      },
      "X-RateLimit-Remaining": {
        "schema": {
          "type": "integer"
        },
        "description": "Requests left for this API key in the current window."
      },
      "X-RateLimit-Reset": {
        "schema": {
          "type": "integer"
        },
        "description": "Unix epoch seconds at which the current window resets."
      },
      "X-RateLimit-Scope": {
        "schema": {
          "type": "string",
          "enum": [
            "api-key",
            "ip"
          ]
        },
        "description": "Which budget these headers describe. `api-key` on every per-key counted response. `ip` only on a 429 from the separate network-level protection against abusive traffic, which sits well above the per-key limits and carries `X-RateLimit-IP-Remaining` instead of the per-key headers."
      },
      "X-RateLimit-IP-Remaining": {
        "schema": {
          "type": "integer"
        },
        "description": "Only present with `X-RateLimit-Scope: ip`. A legitimate integration should never see this header on a 429."
      },
      "X-RateLimit-Rows-Limit": {
        "schema": {
          "type": "integer"
        },
        "description": "Bulk rows budget: CSV data rows (`POST /send/bulk`, synchronous transport) or documents (`POST /receive/bulk`) allowed per 60-second window for this API key."
      },
      "X-RateLimit-Rows-Remaining": {
        "schema": {
          "type": "integer"
        },
        "description": "Rows left in this API key's bulk rows budget for the current window."
      },
      "X-RateLimit-Rows-Reset": {
        "schema": {
          "type": "integer"
        },
        "description": "Unix epoch seconds at which the bulk rows budget window resets."
      },
      "Retry-After": {
        "schema": {
          "type": "integer"
        },
        "description": "Seconds to wait before retrying."
      }
    },
    "responses": {
      "RateLimited": {
        "description": "Rate limit exceeded — the request was not processed. `X-RateLimit-Scope` says which budget fired: `api-key` for this endpoint class's per-minute request budget (or, on `POST /send/bulk` and `POST /receive/bulk`, the bulk rows budget — then the `X-RateLimit-Rows-*` headers are present and the upload was rejected whole), or `ip` for the separate network-level protection against abusive traffic. Wait `Retry-After` seconds and retry. Limits per endpoint class are in the Rate limits section of the API overview.",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/Retry-After"
          },
          "X-RateLimit-Scope": {
            "$ref": "#/components/headers/X-RateLimit-Scope"
          },
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/X-RateLimit-Limit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/X-RateLimit-Remaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/X-RateLimit-Reset"
          },
          "X-RateLimit-Rows-Limit": {
            "$ref": "#/components/headers/X-RateLimit-Rows-Limit"
          },
          "X-RateLimit-Rows-Remaining": {
            "$ref": "#/components/headers/X-RateLimit-Rows-Remaining"
          },
          "X-RateLimit-Rows-Reset": {
            "$ref": "#/components/headers/X-RateLimit-Rows-Reset"
          },
          "X-RateLimit-IP-Remaining": {
            "$ref": "#/components/headers/X-RateLimit-IP-Remaining"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/RateLimitExceededError"
            }
          }
        }
      },
      "FilingUploadConflict": {
        "description": "The month or file cannot take the action. `error` is one of FILE_ALREADY_ADDED, SYSTEM_FILE_NOT_DELETABLE, MONTH_NOT_OPEN (the month is not open yet), MONTH_CLOSED (the upload window has closed), MONTH_LOCKED (carries `reason`), PERIOD_PROCESSING (carries `state`), PERIOD_CONFLICT, PROCESSING_REFUSED (the processing side cannot process the month yet; `message` says why), FILE_ERRORS (one or more of your files have errors: fix and upload again, or delete, then confirm; carries `filesWithErrors`), SYSTEM_FILE_ERRORS (a file prepared for you has errors and cannot be changed by you: contact support; carries `filesWithErrors` and `contactUrl`), SYSTEM_FILE_PENDING (the month's calculated transactions are still being added: try again shortly), month_not_available, filing_backend_not_linked. Each carries a plain-language `message`; the extra fields are top-level body fields, not part of the message.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "error": {
                  "type": "string",
                  "enum": [
                    "FILE_ALREADY_ADDED",
                    "SYSTEM_FILE_NOT_DELETABLE",
                    "MONTH_NOT_OPEN",
                    "MONTH_CLOSED",
                    "MONTH_LOCKED",
                    "PERIOD_PROCESSING",
                    "PERIOD_CONFLICT",
                    "PROCESSING_REFUSED",
                    "FILE_ERRORS",
                    "SYSTEM_FILE_ERRORS",
                    "SYSTEM_FILE_PENDING",
                    "month_not_available",
                    "filing_backend_not_linked"
                  ]
                },
                "message": {
                  "type": "string"
                },
                "existingId": {
                  "type": "string",
                  "description": "FILE_ALREADY_ADDED: the id of the file already in the month."
                },
                "reason": {
                  "type": "string",
                  "description": "FILE_ALREADY_ADDED: `same_name` or `same_content`. MONTH_LOCKED: why the month is locked."
                },
                "state": {
                  "type": "string",
                  "description": "PERIOD_PROCESSING: `processing`."
                },
                "filesWithErrors": {
                  "type": "integer",
                  "description": "FILE_ERRORS, SYSTEM_FILE_ERRORS: how many files in the month have errors."
                },
                "contactUrl": {
                  "type": "string",
                  "description": "SYSTEM_FILE_ERRORS: where to contact support."
                }
              }
            }
          }
        }
      },
      "FilingUploadUnavailable": {
        "description": "Data upload could not reach its processing service, or is not switched on for the account yet (`processing_backend_unavailable`). Try again later.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/FilingError"
            }
          }
        }
      },
      "FilingPaymentRequired": {
        "description": "Filing needs a subscription (`plan_required`). On the filing setup, only the request/replace body is refused; defer and stop are not. Approving a live filing is refused too.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "error": {
                  "type": "string",
                  "enum": [
                    "plan_required"
                  ]
                },
                "reason": {
                  "type": "string",
                  "enum": [
                    "filing_requires_subscription"
                  ]
                },
                "message": {
                  "type": "string",
                  "description": "Same sentence as filingAccess.message."
                },
                "detail": {
                  "type": "string",
                  "description": "Same as message."
                },
                "upgradeUrl": {
                  "type": "string",
                  "description": "Absolute billing page."
                }
              }
            }
          }
        }
      },
      "FilingActionApplied": {
        "description": "The action was applied, or the filing was already in that state (alreadyApplied is true). Approving a live filing (`ret_` id) returns the full refreshed detail in `filing`, including `documents`.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "ok": {
                  "type": "boolean"
                },
                "alreadyApplied": {
                  "type": "boolean"
                },
                "message": {
                  "type": "string",
                  "example": "This return is approved. We will file it before the due date.",
                  "description": "Live filing approval only: display text for the result; render it as is."
                },
                "filing": {
                  "$ref": "#/components/schemas/FilingDetail"
                }
              }
            }
          }
        }
      },
      "FilingDataUnavailable": {
        "description": "`FILING_DATA_UNAVAILABLE`: filing data is temporarily unavailable. Try again shortly.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "error": {
                  "type": "string",
                  "enum": [
                    "FILING_DATA_UNAVAILABLE"
                  ]
                },
                "detail": {
                  "type": "string",
                  "example": "Filing is temporarily unavailable. Try again shortly."
                }
              }
            }
          }
        }
      },
      "FilingApproveForbidden": {
        "description": "`FORBIDDEN`: the role or key may not change filings (live filings).",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "error": {
                  "type": "string",
                  "enum": [
                    "FORBIDDEN"
                  ]
                },
                "detail": {
                  "type": "string",
                  "example": "You do not have permission to approve this return."
                }
              }
            }
          }
        }
      },
      "FilingApproveConflict": {
        "description": "The filing cannot be approved. A stored filing answers FILING_STALE (carries `currentSnapshotVersion`), INVALID_TRANSITION, BANK_DETAILS_REQUIRED or CONTENT_MISSING. A live filing (`ret_` id) answers NOT_AWAITING_APPROVAL (the return is not ready for approval), BANK_DETAILS_REQUIRED or APPROVAL_NOT_COMPLETED (the approval was recorded but the return is not yet approved; our team has been told).",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "error": {
                  "type": "string",
                  "enum": [
                    "FILING_STALE",
                    "INVALID_TRANSITION",
                    "BANK_DETAILS_REQUIRED",
                    "CONTENT_MISSING",
                    "NOT_AWAITING_APPROVAL",
                    "APPROVAL_NOT_COMPLETED"
                  ]
                },
                "detail": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "FilingApprovalUnavailable": {
        "description": "`AUDIT_UNAVAILABLE` (the approval could not be recorded; nothing was approved), `FILING_DATA_UNAVAILABLE` (the return cannot be reached right now) or `APPROVAL_UNCONFIRMED` (the outcome could not be confirmed: check the return before trying again).",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "error": {
                  "type": "string",
                  "enum": [
                    "AUDIT_UNAVAILABLE",
                    "FILING_DATA_UNAVAILABLE",
                    "APPROVAL_UNCONFIRMED"
                  ]
                },
                "detail": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "FilingNotFound": {
        "description": "No such filing for this entity (`FILING_NOT_FOUND`).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/FilingError"
            }
          }
        }
      },
      "FilingValidationError": {
        "description": "The request body or query is invalid (`VALIDATION_ERROR`, `REASON_REQUIRED`, `PREPAYMENT_EXCEEDS_AMOUNT_DUE`).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/FilingError"
            }
          }
        }
      },
      "FilingConflict": {
        "description": "The filing cannot take the action. `error` is one of FILING_STALE (carries `currentSnapshotVersion`), INVALID_TRANSITION (carries `currentStatus`), ALREADY_APPROVED, ALREADY_SUBMITTED, PAUSE_LIMIT_REACHED, PAUSE_PAST_CUTOFF, PAUSE_NOT_ALLOWED, BANK_DETAILS_REQUIRED, CONTENT_MISSING.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "error": {
                  "type": "string",
                  "enum": [
                    "FILING_STALE",
                    "INVALID_TRANSITION",
                    "ALREADY_APPROVED",
                    "ALREADY_SUBMITTED",
                    "PAUSE_LIMIT_REACHED",
                    "PAUSE_PAST_CUTOFF",
                    "PAUSE_NOT_ALLOWED",
                    "BANK_DETAILS_REQUIRED",
                    "CONTENT_MISSING"
                  ]
                },
                "detail": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "FilingPauseConflict": {
        "description": "A stored filing answers as in `Conflict`. A live filing (`ret_` id) answers NOT_AWAITING_APPROVAL (the return is not waiting for approval), PAUSE_NOT_ALLOWED (backdated, not finalised automatically, or already paused once), PAUSE_PAST_CUTOFF, PAUSE_LIMIT_REACHED or NOT_PAUSED (resume only). `detail` is a plain sentence to show as is.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "error": {
                  "type": "string",
                  "enum": [
                    "FILING_STALE",
                    "INVALID_TRANSITION",
                    "ALREADY_APPROVED",
                    "ALREADY_SUBMITTED",
                    "PAUSE_LIMIT_REACHED",
                    "PAUSE_PAST_CUTOFF",
                    "PAUSE_NOT_ALLOWED",
                    "NOT_AWAITING_APPROVAL",
                    "NOT_PAUSED"
                  ]
                },
                "detail": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "FilingPauseValidation": {
        "description": "VALIDATION_ERROR (stored filing) or INVALID_PAUSE_END (live filing: `endDate` is not a date, or outside `allowedEnd`).",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "error": {
                  "type": "string",
                  "enum": [
                    "VALIDATION_ERROR",
                    "INVALID_PAUSE_END"
                  ]
                },
                "detail": {
                  "type": "string"
                },
                "allowedEnd": {
                  "type": "object",
                  "properties": {
                    "earliest": {
                      "type": "string",
                      "format": "date"
                    },
                    "latest": {
                      "type": "string",
                      "format": "date"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "schemas": {
      "Mandate": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "IT-SDI"
          },
          "country": {
            "type": "string",
            "example": "IT"
          },
          "name": {
            "type": "string",
            "example": "Italian SDI (FatturaPA)"
          },
          "authority": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "live",
              "beta",
              "coming_soon"
            ]
          },
          "inputFormats": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "documentTypes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "currencies": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "submissionLatency": {
            "type": "string"
          },
          "notes": {
            "type": "string"
          },
          "requiredFields": {
            "type": "array",
            "description": "Only present in the single-mandate (?id=) view.",
            "items": {
              "type": "object",
              "properties": {
                "path": {
                  "type": "string",
                  "example": "supplier.taxId"
                },
                "description": {
                  "type": "string"
                },
                "required": {
                  "type": "boolean"
                },
                "example": {
                  "type": "string"
                },
                "notes": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "Address": {
        "type": "object",
        "required": [
          "city",
          "country"
        ],
        "properties": {
          "street": {
            "type": "string",
            "example": "Via Roma 1",
            "description": "Street address line 1."
          },
          "city": {
            "type": "string",
            "example": "Milan"
          },
          "postalCode": {
            "type": "string",
            "example": "20121"
          },
          "countyCode": {
            "type": "string",
            "description": "County / region / state code (ISO 3166-2 or local; BT-39 supplier / BT-54 customer). Required for Romania: ISO 3166-2:RO county code, e.g. \"RO-CJ\", or \"RO-B\" for Bucharest. REQUIRED on a Romanian party's address — missing it is stored as a rejection (200, `NEEDS_INFO`) naming this field. A Bucharest (\"RO-B\") party additionally needs countrySpecific.ro.supplierSector / .customerSector.",
            "example": "RO-CJ"
          },
          "country": {
            "type": "string",
            "minLength": 2,
            "maxLength": 2,
            "example": "IT"
          },
          "street2": {
            "type": "string",
            "description": "Street address line 2.",
            "example": "Floor 3"
          },
          "buildingNumber": {
            "type": "string",
            "description": "Building/plot number — Saudi Arabia (ZATCA) detailed-address requirement (4 digits).",
            "example": "8228"
          },
          "district": {
            "type": "string",
            "description": "District / neighbourhood / city subdivision. Required in the ZATCA detailed address.",
            "example": "Al Olaya"
          }
        }
      },
      "Party": {
        "type": "object",
        "required": [
          "name",
          "address"
        ],
        "properties": {
          "name": {
            "type": "string",
            "example": "Acme SRL"
          },
          "taxId": {
            "type": "string",
            "example": "IT12345678901",
            "description": "Tax/VAT identifier. On `supplier` it is OPTIONAL: omit it and the platform applies the entity's own current tax registration for the resolved mandate's country (an entity can hold registrations in several countries — the right one is derived per invoice from the mandate that actually resolves, never assumed from the entity's home country). If you do supply it, it must equal that registered number (a leading country prefix is stripped before comparing, e.g. ESB72756737 and B72756737 are the same NIF) or the document is stored as `REJECTED` with `SUPPLIER_TAX_ID_MISMATCH` — the registered value is never silently substituted for a different asserted one. On `customer` it is strongly recommended for B2B (reverse-charge treatment, SII counterparty identification)."
          },
          "address": {
            "$ref": "#/components/schemas/Address"
          },
          "establishmentCountry": {
            "type": "string",
            "minLength": 2,
            "maxLength": 2,
            "description": "ISO 3166-1 alpha-2 country of the tax registration that issued `taxId`. Optional — derived from the `taxId` prefix when omitted. Spain VeriFactu: required together with a passport, residence or other identifier (`countrySpecific.es.customerIdType`).",
            "example": "IT"
          },
          "electronicAddress": {
            "type": "object",
            "description": "Peppol electronic address (BT-34 seller, BT-49 buyer): send `value` and `schemeId` together. Meaningful on `customer` only (required when auto-derivation is not supported for the customer's country) — ignored on `supplier`, which always uses the entity's own confirmed Peppol Participant ID (set once under the dashboard's Settings → Account → Peppol Network Identity section) rather than a per-request value, so a caller can never claim a different sender identity than the one the entity has confirmed. The document is stored as `NEEDS_INFO` with `MISSING_SELLER_PEPPOL_ID` if the entity has none confirmed.",
            "properties": {
              "value": {
                "type": "string",
                "description": "Peppol participant ID for the scheme in `schemeId`.",
                "example": "0208:0123456789"
              },
              "schemeId": {
                "type": "string",
                "description": "Peppol EAS scheme ID.",
                "example": "0208"
              }
            }
          },
          "contact": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string"
              },
              "email": {
                "type": "string",
                "format": "email"
              },
              "phone": {
                "type": "string"
              }
            }
          },
          "customerRef": {
            "type": "string",
            "description": "Customer only (ignored on supplier). Your own reference for a previously-saved customer record — when provided, Clearvo looks it up and fills in any of name/taxId/address you omit from this customer object. If the stored customer has a human-confirmed PEPPOL_PARTICIPANT_ID reference, it also fills in customer.electronicAddress, and a stored LEITWEG_ID reference fills in countrySpecific.de.leitwegId; each fills in only when the request supplies neither the explicit value (the electronicAddress pair; countrySpecific.de.leitwegId or a non-empty buyerReference), and an explicit value always wins. See GET /v1/customer-reference-types. For an Italy (IT) invoice, it also fills in countrySpecific.it.codiceDestinatario/pecDestinatario from the stored customer when present. Fields you do supply directly always take precedence over the stored ones. Manage customers via GET/POST /v1/customers and GET/PATCH/DELETE /v1/customers/{id}, or from the dashboard's Customers page — every successfully-issued invoice also auto-captures/refreshes a customer record from its customer details, whether or not customerRef was supplied.",
            "example": "CUST-001"
          },
          "supplierRef": {
            "type": "string",
            "description": "Supplier only (ignored on customer). Your own reference for a previously-saved supplier record — when provided, Clearvo looks it up and fills in any of name/taxId/address you omit from this supplier object. Fields you do supply directly always take precedence over the stored ones. Primarily used on a self-billed invoice (countrySpecific.peppol.selfBilling), where `supplier` identifies the real third-party seller rather than the entity itself. Manage suppliers via GET/POST /v1/suppliers and GET/PATCH/DELETE /v1/suppliers/{id}, or PUT /v1/suppliers/by-ref/{ref}.",
            "example": "VEND-001"
          },
          "legalRegistrationId": {
            "type": "string",
            "description": "Legal registration ID, e.g. a Companies House number or SIRET (BT-30).",
            "example": "12345678"
          },
          "identifiers": {
            "type": "array",
            "description": "Additional party identifiers — BT-29 (seller) / BT-46 (buyer) — distinct from legalRegistrationId and taxId. For Saudi Arabia (ZATCA) the scheme is one of CRN, MOM, MLS, 700, SAG, NAT, GCC, IQA, PAS, OTH.",
            "items": {
              "type": "object",
              "required": [
                "value"
              ],
              "properties": {
                "scheme": {
                  "type": "string",
                  "example": "CRN"
                },
                "value": {
                  "type": "string",
                  "example": "1010010000"
                }
              }
            }
          }
        }
      },
      "LineItem": {
        "type": "object",
        "description": "One `lines[]` entry on POST /send. Field names are tax-type-neutral: the retired `vatRate`/`vatAmount` names are stored as `REJECTED` with `UNKNOWN_FIELD_VAT_RENAMED` (naming `taxRate`/`taxAmount`), and the retired `discount`, `unit`, `itemCode` and `exemption` names with `UNKNOWN_FIELD_LINE_RENAMED` (naming `discountPercent`/`discountAmount`, `unitOfMeasure`, `sellerItemId`, and `taxTreatment`/`clientTaxCode` respectively) — never silently ignored, no alias. A line never carries a `taxCode` (400 UNSUPPORTED_FIELD) — the EN16931 category is a resolved OUTPUT, reported per line in `taxResolution`.",
        "required": [
          "description",
          "quantity",
          "unitPrice",
          "taxRate"
        ],
        "properties": {
          "description": {
            "type": "string",
            "example": "Software licence — annual"
          },
          "quantity": {
            "type": "number",
            "example": 1
          },
          "unitPrice": {
            "type": "number",
            "description": "Unit price excluding tax (net). When `taxIncluded` is true it is the gross price and the net is back-computed as unitPrice / (1 + taxRate / 100).",
            "example": 1000.0
          },
          "taxRate": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "description": "Tax rate as a percentage (e.g. 22 for 22%, 0 for a reverse-charge line). Required on EVERY line — even one whose treatment resolves from `clientTaxCode` still carries its own observed rate; it is never computed for you (a missing rate, or one outside 0–100, is a 400 payload error naming `lines[i].taxRate`). A POSITIVE rate with no clientTaxCode/taxTreatment is reported as a domestic taxable supply at that rate — your rate is authoritative and is never rejected as an unrecognised rate. A 0% line with no clientTaxCode/taxTreatment is rejected (400 `ZERO_RATE_NEEDS_TAX_TREATMENT`) — a bare 0% rate alone does not state why it is zero, so supply `taxTreatment` (exempt / zero_rated / reverse_charge / out_of_scope, or the relevant cross-border movement via a client tax code); the platform never guesses. The retired `vatRate` name is stored as `REJECTED` with `UNKNOWN_FIELD_VAT_RENAMED` naming `taxRate` — there is no alias. A line `taxCode` is never accepted (400 `UNSUPPORTED_FIELD`): the EN16931 category is always a resolved OUTPUT — see `taxResolution` on the response.",
            "example": 22
          },
          "taxAmount": {
            "type": "number",
            "description": "Tax amount for this line in the invoice currency. Optional — when omitted it is computed as round(lineTotal × taxRate / 100); supply it explicitly to control rounding precisely. The retired `vatAmount` name is stored as `REJECTED` with `UNKNOWN_FIELD_VAT_RENAMED` naming `taxAmount`.",
            "example": 220.0
          },
          "clientTaxCode": {
            "type": "string",
            "description": "RECOMMENDED. Your own ERP tax code (e.g. a SAP two-digit code), configured in advance via POST /tax/client-codes — lets you send the code your own system already knows rather than reasoning about tax categories at all. Mutually exclusive with `taxTreatment` — set at most one of the two on a single line (a line with neither is resolved from its stated facts — `taxRate` plus any `taxTreatment` — mapped as given, never inferred from the customer/seller country pair). An unrecognised code for THIS entity is never rejected outright: the invoice is accepted and the record is held (`clearanceStatus: HELD_UNMAPPED_TAX_CODE`) with a machine-readable reason and a deep link to configure the code — see the ClearanceStatus and UnmappedTaxCodeReason schemas — since an unconfigured code is a setup gap, not a malformed request.",
            "example": "A1"
          },
          "taxTreatment": {
            "allOf": [
              {
                "$ref": "#/components/schemas/TaxTreatment"
              }
            ],
            "description": "The AUTHORITATIVE tax treatment for this line, used only when `clientTaxCode` is omitted. The stated treatment (exempt / out_of_scope / zero_rated / reverse_charge) is mapped as-is — the platform does not infer or override the movement from the customer/seller countries. Required whenever this line's `taxRate` is 0: a 0% line with no `clientTaxCode`/`taxTreatment` is rejected (400 `ZERO_RATE_NEEDS_TAX_TREATMENT`) rather than guessed. Mutually exclusive with `clientTaxCode`. An unrecognized value is a 400 naming this field."
          },
          "customerType": {
            "type": "string",
            "enum": [
              "B2B",
              "B2C"
            ],
            "description": "Per-line override of the invoice-level `customerType` — business vs. consumer — consulted only when this line has no `clientTaxCode`. Defaults to the invoice-level `customerType`, then to whether `customer.taxId` is present."
          },
          "supplyType": {
            "type": "string",
            "enum": [
              "goods",
              "digital_service",
              "general_service"
            ],
            "default": "goods",
            "description": "Supply type consulted only when this line has no `clientTaxCode` — affects reverse-charge/place-of-supply treatment for cross-border B2B services."
          },
          "discountPercent": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "description": "Discount on this line as a percentage (0–100). Mutually exclusive with `discountAmount` — supplying both is rejected with `LINE_DISCOUNT_CONFLICT`.",
            "example": 0
          },
          "discountAmount": {
            "type": "number",
            "minimum": 0,
            "description": "Discount on this line as a fixed amount in the invoice currency (>= 0). Mutually exclusive with `discountPercent`.",
            "example": 0
          },
          "unitOfMeasure": {
            "type": "string",
            "description": "UN/ECE Recommendation 20 unit code: EA (each), HUR (hour), DAY, KGM (kilogram), MTR (metre), C62 (unit).",
            "example": "EA"
          },
          "lineNumber": {
            "type": "integer",
            "minimum": 1,
            "description": "1-based line position. Auto-assigned sequentially when omitted; must be a positive integer when supplied.",
            "example": 1
          },
          "sellerItemId": {
            "type": "string",
            "description": "Your own product/item identifier for this line (EN16931 BT-155).",
            "example": "SKU-001"
          },
          "gtuCode": {
            "type": "string",
            "enum": [
              "GTU_01",
              "GTU_02",
              "GTU_03",
              "GTU_04",
              "GTU_05",
              "GTU_06",
              "GTU_07",
              "GTU_08",
              "GTU_09",
              "GTU_10",
              "GTU_11",
              "GTU_12",
              "GTU_13"
            ],
            "description": "Poland (PL) FA(3)/KSeF only — this line's GTU goods/service classification. At most one code per line (the FA(3) schema's GTU element is single-valued). `countrySpecific.pl.gtuCodes` (invoice-level) is a fallback ONLY when it carries exactly one code and no line sets its own `gtuCode`; several invoice-level codes with no line assignment is rejected with `PL_GTU_LINE_ASSIGNMENT_REQUIRED`."
          },
          "lineType": {
            "type": "string",
            "enum": [
              "product",
              "service",
              "digital_service",
              "transport",
              "other"
            ],
            "description": "Type of line item — used for tax treatment in some jurisdictions (e.g. digital services; Greece checks it against the resolved goods/service classification)."
          },
          "taxIncluded": {
            "type": "boolean",
            "description": "Per-line override for tax-inclusive pricing. When true, unitPrice is gross and taxRate must be set; the net price is back-computed as unitPrice / (1 + taxRate / 100). Inherits the invoice-level taxIncluded when omitted."
          },
          "gtin": {
            "type": "string",
            "description": "GTIN barcode (BT-157)."
          },
          "itemClassificationCode": {
            "type": "string",
            "description": "Product classification code, e.g. UNSPSC or CPV (BT-158). Poland also accepts PKWiU or CN. Requires itemClassificationScheme."
          },
          "itemClassificationScheme": {
            "type": "string",
            "description": "Classification scheme, e.g. UNSPSC, CPV or STI (GTIN). Required with itemClassificationCode."
          },
          "orderLineReference": {
            "type": "string",
            "description": "Purchase-order line reference (BT-132)."
          },
          "period": {
            "type": "object",
            "description": "Billing/service period for this line (BT-134/135).",
            "required": [
              "start",
              "end"
            ],
            "properties": {
              "start": {
                "type": "string",
                "format": "date"
              },
              "end": {
                "type": "string",
                "format": "date"
              }
            }
          },
          "note": {
            "type": "string",
            "description": "Line-level note (BT-127)."
          },
          "metadata": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Per-line metadata — stored internally, never sent to the authority."
          },
          "allowances": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AllowanceCharge"
            },
            "description": "Line-level allowances (further discounts applied to the line total)."
          },
          "charges": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AllowanceCharge"
            },
            "description": "Line-level charges (surcharges applied to the line total)."
          },
          "sourceSystem": {
            "type": "string",
            "description": "Which connector/ERP the line's clientTaxCode was read from (e.g. sap). Diagnostic only: echoed as source_system in a HELD_UNMAPPED_TAX_CODE reason; never changes which code resolves."
          },
          "sourceSystemCode": {
            "type": "object",
            "required": [
              "system",
              "code"
            ],
            "description": "A connector's own raw tax-code value (e.g. Xero's TaxType OUTPUT2), resolved through the connector's system vocabulary. Mutually exclusive with clientTaxCode and taxTreatment.",
            "properties": {
              "system": {
                "type": "string"
              },
              "code": {
                "type": "string"
              }
            }
          },
          "clientTaxCodeSource": {
            "type": "string",
            "enum": [
              "caller",
              "default"
            ],
            "description": "Who chose this line's clientTaxCode. Omitted (read as caller) for any code you typed; 'default' is set only by the platform's own bulk CSV pipeline when a blank tax_code was replaced with a default. Diagnostic only."
          }
        }
      },
      "TaxCode": {
        "type": "string",
        "description": "EN16931 tax category code.",
        "enum": [
          "S",
          "AA",
          "AE",
          "E",
          "K",
          "G",
          "Z",
          "O"
        ],
        "x-enumDescriptions": {
          "S": "Standard rate",
          "AA": "Reduced rate",
          "AE": "Reverse charge (VAT reported by customer)",
          "E": "Exempt",
          "K": "Intra-EU supply (zero-rated, VAT reported by customer in destination country)",
          "G": "Export outside EU (zero-rated)",
          "Z": "Zero-rated (not exempt)",
          "O": "Outside scope of VAT"
        }
      },
      "TaxTreatment": {
        "type": "string",
        "description": "The AUTHORITATIVE tax treatment for a line, `shipping`, or an allowance/charge — used only when `clientTaxCode` is omitted, to state why a 0% line is zero. Mapped as-is: the platform does not infer or override it from the customer/seller countries. Not an EN16931 output code (that is always a resolved OUTPUT, never a caller-supplied value) — this is an authoritative INPUT declaration of which kind of 0% sale this is.",
        "enum": [
          "exempt",
          "out_of_scope",
          "zero_rated",
          "reverse_charge"
        ],
        "x-enumDescriptions": {
          "exempt": "Exempt from VAT under a specific statutory exemption (e.g. certain financial, medical, or educational services).",
          "out_of_scope": "Outside the scope of VAT entirely — not a supply for VAT purposes.",
          "zero_rated": "A taxable supply charged at a 0% rate (distinct from exempt — still a taxable transaction, just at 0%).",
          "reverse_charge": "Domestic or cross-border reverse charge — VAT is reported by the customer, not charged by the seller."
        }
      },
      "Shipping": {
        "type": "object",
        "description": "Shipping / freight — treated as a document-level charge with carrier metadata. Not the same as a delivery address (set in `delivery`).",
        "properties": {
          "amount": {
            "type": "number",
            "description": "Net shipping cost (excl. tax), or gross when the invoice has taxIncluded=true and taxRate is set.",
            "example": 9.99
          },
          "trackingNumber": {
            "type": "string",
            "description": "Tracking number or waybill reference."
          },
          "clientTaxCode": {
            "type": "string",
            "description": "Same resolution as line.clientTaxCode — RECOMMENDED. See that field's own description."
          },
          "taxTreatment": {
            "allOf": [
              {
                "$ref": "#/components/schemas/TaxTreatment"
              }
            ],
            "description": "Same resolution as line.taxTreatment. Unlike a line item, shipping with neither this nor `clientTaxCode` set defaults to zero-rated rather than a 400 — shipping never drives Compliance Mandate resolution, so this is a deliberately lenient carve-out."
          },
          "carrierId": {
            "type": "string",
            "description": "Carrier identifier code (IATA carrier code or similar).",
            "example": "DHL"
          },
          "description": {
            "type": "string",
            "description": "Description shown on the invoice, e.g. \"DHL Express Delivery\"."
          },
          "serviceLevel": {
            "type": "string",
            "description": "Service level: STANDARD, EXPRESS, OVERNIGHT or ECONOMY."
          },
          "taxRate": {
            "type": "number",
            "description": "Tax rate as a percentage (e.g. 21). Required if shipping carries non-zero tax."
          },
          "taxAmount": {
            "type": "number",
            "description": "Tax amount on shipping. Computed from taxRate x net amount if omitted."
          }
        }
      },
      "AllowanceCharge": {
        "type": "object",
        "description": "A document-level allowance (discount) or charge (surcharge) applied to the invoice total — BG-20/BG-21.",
        "required": [
          "amount"
        ],
        "properties": {
          "amount": {
            "type": "number",
            "description": "Absolute amount, always positive — sign is implied by whether this appears under `allowances` or `charges`.",
            "example": 10.0
          },
          "reason": {
            "type": "string",
            "example": "Early payment discount",
            "description": "Free-text reason. Optional."
          },
          "clientTaxCode": {
            "type": "string",
            "description": "Same resolution as line.clientTaxCode — RECOMMENDED. See that field's own description."
          },
          "taxTreatment": {
            "allOf": [
              {
                "$ref": "#/components/schemas/TaxTreatment"
              }
            ],
            "description": "Same resolution as line.taxTreatment. Unlike a line item, an allowance/charge with neither this nor `clientTaxCode` set defaults to zero-rated rather than a 400 — never drives Compliance Mandate resolution, so this is a deliberately lenient carve-out."
          },
          "baseAmount": {
            "type": "number",
            "description": "Base amount the percentage is computed from."
          },
          "percentage": {
            "type": "number",
            "description": "Percentage rate (e.g. 5 for 5%)."
          },
          "reasonCode": {
            "type": "string",
            "description": "UNCL5189 reason code (allowances) or UNCL7161 (charges)."
          },
          "taxRate": {
            "type": "number",
            "description": "Tax rate applicable to this allowance/charge, as a percentage (e.g. 21)."
          },
          "taxAmount": {
            "type": "number",
            "description": "Tax amount on this allowance/charge. Computed from taxRate if omitted."
          }
        }
      },
      "PaymentMeans": {
        "type": "object",
        "description": "One way of paying the invoice (BG-16). Send one entry per way of paying in `payment.means[]`.",
        "properties": {
          "method": {
            "type": "string",
            "enum": [
              "bank_transfer",
              "direct_debit",
              "credit_card",
              "cash",
              "check",
              "other"
            ],
            "description": "'direct_debit' renders SEPA payment means (UBL/CII PaymentMeansCode 59) and requires mandateReference (BT-89) — Germany also requires creditorId and debitedIban (BT-90/91). Omit for a plain credit-transfer invoice (the default)."
          },
          "iban": {
            "type": "string",
            "example": "IT60X0542811101000000123456"
          },
          "bic": {
            "type": "string",
            "example": "BPCVIT2S",
            "description": "BIC/SWIFT code. Forbidden on a German XRechnung invoice (BR-DE-25-b, rejected with DE_XRECHNUNG_BIC_FORBIDDEN): omit it for DE."
          },
          "reference": {
            "type": "string",
            "example": "INV-2024-001",
            "description": "Payment reference / remittance information (BT-83)."
          },
          "mandateReference": {
            "type": "string",
            "maxLength": 35,
            "example": "MNDT-2026-0042",
            "description": "SEPA mandate reference identifier (BT-89, BG-19 Direct Debit). Required whenever method is 'direct_debit' — max 35 characters. Validation errors name it as `payment.means[i].mandateReference`."
          },
          "creditorId": {
            "type": "string",
            "example": "DE98ZZZ09999999999",
            "description": "SEPA bank-assigned creditor identifier / Gläubiger-ID (BT-90) — the seller's own identifier, never the customer's. Germany additionally requires this whenever method is 'direct_debit'."
          },
          "debitedIban": {
            "type": "string",
            "example": "DE89370400440532013000",
            "description": "The customer's own IBAN the SEPA direct debit draws from (BT-91). Must be a real IBAN — Germany additionally requires this whenever method is 'direct_debit'. Distinct from `iban` above, which is the seller's own account for a bank_transfer payment. Validation errors name it as `payment.means[i].debitedIban`."
          }
        }
      },
      "PaymentTerms": {
        "type": "object",
        "properties": {
          "text": {
            "type": "string",
            "example": "Net 30",
            "description": "Free-text payment terms (BT-20)."
          },
          "earlyPayment": {
            "type": "object",
            "description": "Early-payment discount (Skonto) as structured data.",
            "properties": {
              "days": {
                "type": "number",
                "example": 14,
                "description": "Pay within this many days to earn the discount."
              },
              "percent": {
                "type": "number",
                "example": 2,
                "description": "Discount percentage."
              },
              "baseAmount": {
                "type": "number",
                "description": "Explicit base amount; defaults to the invoice's tax-exclusive amount."
              }
            }
          }
        }
      },
      "Payment": {
        "type": "object",
        "description": "Payment details. The pre-`payment.means[]` shape is stored as `REJECTED` with `UNKNOWN_FIELD_RENAMED`, one `errors[]` entry per retired key naming its replacement: `payment.method|iban|bic|reference|mandateReference|creditorId|debitedIban` are now `payment.means[].<same name>`; a string `payment.terms` is now `payment.terms.text` (an object `payment.terms` is the current shape); `payment.skonto.days|percentOff|baseAmount` are now `payment.terms.earlyPayment.days|percent|baseAmount`; `payment.dueDate` is removed (use the top-level `dueDate`). No alias.",
        "properties": {
          "means": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PaymentMeans"
            },
            "description": "Payment instructions (BG-16), one entry per way of paying."
          },
          "terms": {
            "$ref": "#/components/schemas/PaymentTerms"
          },
          "installments": {
            "type": "array",
            "description": "Payment installment schedule. The amounts should sum to the invoice total.",
            "items": {
              "type": "object",
              "properties": {
                "dueDate": {
                  "type": "string",
                  "format": "date"
                },
                "amount": {
                  "type": "number"
                },
                "description": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "Adjustment": {
        "type": "object",
        "description": "An auto-correction silently applied to the invoice before submission by a content-team-managed ADJUSTMENT rule. Set rejectInsteadOfAutoCorrect on the request to opt out and receive a blocking entry in `errors` instead of a silent correction. Never applied to a tax-determinative field — amounts, rates, tax category, issueDate, currency, or the invoice's taxation country are never auto-corrected.",
        "properties": {
          "ruleCode": {
            "type": "string",
            "description": "Internal validation-rules registry identifier for the rule that fired.",
            "example": "TAX_ID_CLEANUP"
          },
          "ruleVersion": {
            "type": "integer",
            "description": "The rule's own version at the moment it fired — resolvable to the exact rule definition even after a later content-team edit bumps the version."
          },
          "fieldPath": {
            "type": "string",
            "description": "Dot-path into the submitted invoice (not a JSON Pointer), e.g. 'customer.taxId'.",
            "example": "customer.taxId"
          },
          "before": {
            "type": "string",
            "description": "The value exactly as submitted, before correction."
          },
          "after": {
            "type": "string",
            "description": "The value actually used for XML generation and submission, after correction."
          },
          "errorCode": {
            "type": "string",
            "description": "Stable code identifying which check produced this adjustment — same errorCode namespace as ValidationCheckResult.",
            "example": "TAX_ID_ADJUSTED"
          },
          "suggestedAction": {
            "type": "string",
            "description": "Human-readable explanation of what was changed and why."
          },
          "tier": {
            "type": "string",
            "enum": [
              "TIER1",
              "NORMALIZATION"
            ],
            "description": "NORMALIZATION for a data-hygiene cleanup (country/currency/US-state/postal-code formatting); TIER1 for every other adjustment (tax-ID cleanup, rounding-tolerance absorption, a custom entity-scoped rule)."
          },
          "category": {
            "type": "string",
            "description": "The firing rule's own registry category, verbatim — same namespace as ValidationCatalogueEntry.category, e.g. 'TAX_ID_CLEANUP', 'GENERAL_NORMALIZATION', 'CROSS_FIELD'.",
            "example": "TAX_ID_CLEANUP"
          },
          "params": {
            "type": "object",
            "nullable": true,
            "additionalProperties": true,
            "description": "The exact parameters the firing rule's transform used to produce this correction, verbatim. Null when the rule carries no transform parameters."
          },
          "checkFamily": {
            "type": "string",
            "description": "Groups this rule with every other row of the same conceptual check (e.g. every per-country variant of one check shares a checkFamily) — same namespace as ValidationCatalogueEntry's own grouping. Falls back to the rule's own ruleCode when it isn't part of a family.",
            "example": "TAX_ID_CLEANUP"
          },
          "legalBasisTag": {
            "type": "string",
            "enum": [
              "LEGAL_MANDATE",
              "BUSINESS_POLICY"
            ],
            "description": "Whether this rule derives from an authority's own requirement, or is Clearvo's own operational choice. Always BUSINESS_POLICY for an Adjustment — auto-correction can never be the permanent, unoverridable kind of rule a real authority mandate requires."
          }
        },
        "required": [
          "ruleCode",
          "ruleVersion",
          "fieldPath",
          "before",
          "after",
          "errorCode",
          "suggestedAction",
          "tier",
          "category",
          "params",
          "checkFamily",
          "legalBasisTag"
        ]
      },
      "ValidationCheckResult": {
        "type": "object",
        "description": "A single failed (ERROR) or annotated (WARNING) check on a submitted invoice, as it appears in POST /v1/send's `errors`/`warnings` arrays.\n\n**No-rename commitment**: once shipped in this API version (v1), an errorCode is never renamed and never repurposed to mean a different check. A retired check stops firing rather than having its errorCode reassigned to something new — codes are added additively. Safe to branch application logic on long-term.\n\n**Exception (2026-09-21)**: as part of the platform-wide supplier/customer vocabulary change, every code containing BUYER was renamed to CUSTOMER (e.g. MISSING_BUYER_NAME -> MISSING_CUSTOMER_NAME, BUYER_SELLER_SAME_TAX_ID -> CUSTOMER_SUPPLIER_SAME_TAX_ID) with no alias; see the changelog for the full list. The additive-only commitment holds from that date.",
        "properties": {
          "errorCode": {
            "type": "string",
            "description": "Stable check identifier. See GET /v1/validations for the full, live catalogue of every code this API can emit.",
            "example": "MISSING_SELLER_TAX_ID"
          },
          "severity": {
            "type": "string",
            "enum": [
              "ERROR",
              "WARNING"
            ],
            "description": "ERROR entries (in `errors`) stop the document being submitted: it is stored as `REJECTED` or `NEEDS_INFO` and answered with HTTP 200. WARNING entries (in `warnings`) never block submission — they annotate the response only, including on a success."
          },
          "path": {
            "type": "string",
            "description": "RFC 6901 JSON Pointer into the request body identifying the offending field.",
            "example": "/customer/taxId"
          },
          "message": {
            "type": "string",
            "description": "Human-readable description of the failure."
          },
          "suggestedAction": {
            "type": "string",
            "description": "What to do to resolve this check — often identical to `message`."
          },
          "fixUrl": {
            "type": "string",
            "nullable": true,
            "description": "Deep link into the Clearvo dashboard where a merchant can resolve this. Null when there's no dedicated dashboard page for it."
          },
          "isMerchantResolvable": {
            "type": "boolean",
            "description": "True when the merchant (not the integrator's own code) is the one who needs to act. Present on `errors` entries only."
          },
          "checkFamily": {
            "type": "string",
            "description": "Groups this entry with every other row of the same conceptual check (e.g. every per-country INVALID_CUSTOMER_TAX_ID_<cc> match shares checkFamily 'INVALID_CUSTOMER_TAX_ID') — same namespace as ValidationCatalogueEntry's own grouping. Lets an integrator route/aggregate outcomes by check identity without a second lookup against GET /v1/validations. Omitted only for a structural/shape check that fires before the content-team-editable rule registry is ever consulted.",
            "example": "INVALID_CUSTOMER_TAX_ID"
          },
          "legalBasisTag": {
            "type": "string",
            "enum": [
              "LEGAL_MANDATE",
              "PLATFORM_INVARIANT",
              "BUSINESS_POLICY"
            ],
            "description": "Whether this check derives from an authority's own requirement (LEGAL_MANDATE), a platform-structural invariant such as a shape or vocabulary check (PLATFORM_INVARIANT), or is Clearvo's own operational choice (BUSINESS_POLICY). Omitted under the same structural-check exception as checkFamily."
          },
          "ruleName": {
            "type": "string",
            "description": "Present on an entry produced by a rule (source `rule`): the rule's own name, the message a caller reads."
          },
          "source": {
            "type": "string",
            "enum": ["rule"],
            "description": "Present with `ruleName` when the entry was produced by a rule rather than a registry check."
          },
          "clearanceStatus": {
            "type": "string",
            "description": "Present with `ruleName`: the status the invoice record takes when this rule rejects it, REJECTED (a defect in the document) or NEEDS_INFO (a required field is missing and Fix & Resubmit applies)."
          }
        },
        "required": [
          "errorCode",
          "severity",
          "path",
          "message",
          "suggestedAction"
        ]
      },
      "ValidationCatalogueEntry": {
        "type": "object",
        "description": "One entry in GET /v1/validations's `checks` array — a check *definition* (what could fire), as opposed to ValidationCheckResult which is a check *result* (what did fire on one specific invoice). Merges the platform's own structural/shape and legal-mandate checks (seeded Global rules, evaluated on every send) with business-rule checks from Clearvo's content-team-editable registry (can be added or disabled without a deploy — always call this endpoint fresh rather than caching it long-term).",
        "properties": {
          "code": {
            "type": "string",
            "description": "Stable code — same namespace and no-rename commitment as ValidationCheckResult.errorCode. This is the exact value a real rejection response carries for this check.",
            "example": "MISSING_SELLER_TAX_ID"
          },
          "originTags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Where this check comes from: ['STRUCTURAL'] for a fixed shape/format check, or ['REGISTRY', 'BUILTIN'|'DECLARATIVE'] for a content-team-editable business rule.",
            "example": [
              "STRUCTURAL"
            ]
          },
          "severity": {
            "type": "string",
            "enum": [
              "ERROR",
              "WARNING",
              "ADJUSTMENT"
            ],
            "description": "ADJUSTMENT means this check silently auto-corrects the field rather than rejecting the invoice — see autoCorrectable."
          },
          "countries": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "nullable": true,
            "description": "Null = applies to every destination country. Otherwise the exact set of ISO 3166-1 alpha-2 codes this check is scoped to."
          },
          "fieldPath": {
            "type": "string",
            "description": "Dot-path template into the request body. '{i}' stands in for a numeric line index — never a literal value.",
            "example": "lines[{i}].clientTaxCode"
          },
          "fieldPointer": {
            "type": "string",
            "description": "RFC 6901 JSON Pointer form of fieldPath, matching the `path` a real rejection response would carry for this same check."
          },
          "autoCorrectable": {
            "type": "boolean",
            "description": "True only when severity is ADJUSTMENT — the one case where this check can silently fix the input (visible in the response's `adjustments[]`) rather than reject it."
          },
          "messageTemplate": {
            "type": "string",
            "description": "Human-facing description. May carry '{name}' placeholders for a structural check (e.g. '{documentType}')."
          },
          "ruleCode": {
            "type": "string",
            "description": "Registry-only. The specific rule's own identifier — several rules can share one `code` (e.g. every per-country MISSING_COUNTRY_CREDENTIALS_<cc> rule shares errorCode MISSING_COUNTRY_CREDENTIALS); ruleCode disambiguates which one fired. Omitted for a STRUCTURAL entry."
          },
          "category": {
            "type": "string",
            "description": "Registry-only classification, e.g. 'FIELD_PRESENCE', 'CREDENTIALS', 'TAX_ID_CLEANUP'. Omitted for a STRUCTURAL entry."
          },
          "legalBasisTag": {
            "type": "string",
            "enum": [
              "LEGAL_MANDATE",
              "BUSINESS_POLICY"
            ],
            "description": "Registry-only. Whether this check derives from an authority's own requirement, or is Clearvo's own operational choice. Omitted for a STRUCTURAL entry."
          },
          "scopeTag": {
            "type": "string",
            "enum": [
              "GENERAL",
              "EINVOICE_SPECIFIC"
            ],
            "description": "Registry-only. GENERAL = shared tax-data-quality logic; EINVOICE_SPECIFIC = permanently e-invoicing-owned. Omitted for a STRUCTURAL entry."
          }
        },
        "required": [
          "code",
          "originTags",
          "severity",
          "countries",
          "fieldPath",
          "fieldPointer",
          "autoCorrectable",
          "messageTemplate"
        ],
        "x-knownErrorCodes": {
          "description": "Every code the platform's seeded structural rules emit as of this spec's last regeneration — structural checks (never change without a deploy) plus the built-in registry seed's errorCodes (deduped; several per-country ruleCodes can share one errorCode). ROUNDING_RESIDUAL_OVER_TOLERANCE in the registry list is a WARNING (the stated payable amount differs from the lines-based recompute by more than one minor unit), not an error, and ROUNDING_RESIDUAL_ABSORBED no longer exists. Generator hold codes (a stored `NEEDS_INFO` with the code in `error.code`) are not listed here; the newest is PAYABLE_AMOUNT_BR_CO_16 (the stated payable amount would fail EN 16931 BR-CO-16 in the target format). Content-team-added registry rules can introduce additional codes at runtime without a deploy — call GET /v1/validations for the live, authoritative, up-to-the-minute catalogue rather than treating this list as exhaustive. Codes are additive-only: see the no-rename commitment on ValidationCheckResult. Exception (2026-09-21): as part of the platform-wide supplier/customer vocabulary change, every code containing BUYER was renamed to CUSTOMER (e.g. MISSING_BUYER_NAME -> MISSING_CUSTOMER_NAME, BUYER_SELLER_SAME_TAX_ID -> CUSTOMER_SUPPLIER_SAME_TAX_ID) with no alias; see the changelog for the full list. The additive-only commitment holds from that date.",
          "structural": [
            "UNKNOWN_FIELD_BUYER_RENAMED",
            "UNKNOWN_FIELD_VAT_RENAMED",
            "UNKNOWN_FIELD_LINE_RENAMED",
            "UNKNOWN_FIELD_RENAMED",
            "UNKNOWN_FIELD_REMOVED",
            "MISSING_INVOICE_NUMBER",
            "INVALID_ISSUE_DATE_FORMAT",
            "INVALID_TAX_POINT_DATE_FORMAT",
            "INVALID_DUE_DATE_FORMAT",
            "INVALID_CURRENCY_CODE",
            "INVALID_TAX_REPORTING_CURRENCY_CODE",
            "EXCHANGE_RATE_AND_TAX_REPORTING_BOTH_SUPPLIED",
            "INVALID_EXCHANGE_RATE",
            "MISSING_TAX_REPORTING_TAX_EXCLUSIVE_AMOUNT",
            "MISSING_TAX_REPORTING_TAX_TOTAL",
            "INVALID_TAX_REPORTING_TOTAL",
            "INVALID_COUNTRY_CODE",
            "NEGATIVE_PREPAID_AMOUNT",
            "NEGATIVE_STATED_PAYABLE_AMOUNT",
            "INVALID_OSS_DECLARED",
            "INVALID_DOCUMENT_TYPE",
            "MISSING_ORIGINAL_INVOICE_NUMBER",
            "MISSING_ORIGINAL_INVOICE_NUMBER_DEBIT_NOTE",
            "MISSING_ORIGINAL_ISSUE_DATE",
            "CREDIT_NOTE_PEPPOL_UNSUPPORTED",
            "CREDIT_NOTE_HU_UNVERIFIED",
            "CREDIT_NOTE_UNSUPPORTED",
            "MISSING_SUPPLIER_DATA",
            "MISSING_SELF_BILLING_SUPPLIER",
            "MISSING_SUPPLIER_NAME",
            "MISSING_SUPPLIER_ADDRESS_CITY",
            "MISSING_SUPPLIER_ADDRESS_COUNTRY",
            "INVALID_SUPPLIER_ADDRESS_COUNTRY",
            "INVALID_CUSTOMER_ADDRESS_COUNTRY",
            "INVALID_SHIP_FROM_COUNTRY",
            "INVALID_SHIP_TO_COUNTRY",
            "MISSING_LINE_ITEMS",
            "MISSING_LINE_DESCRIPTION",
            "INVALID_LINE_QUANTITY",
            "INVALID_LINE_UNIT_PRICE",
            "INVALID_LINE_NUMBER",
            "LINE_DISCOUNT_CONFLICT",
            "INVALID_LINE_DISCOUNT_PERCENT",
            "INVALID_LINE_DISCOUNT_AMOUNT",
            "INVALID_LINE_SUPPLY_TYPE",
            "UNSUPPORTED_FIELD",
            "INVALID_VAT_RATE",
            "AMBIGUOUS_TAX_CODE_FIELDS",
            "INVALID_IT_PARTITA_IVA",
            "INVALID_IT_ESIGIBILITA_IVA",
            "INVALID_PL_NIP",
            "INVALID_PT_NIF",
            "INVALID_GR_AFM",
            "MISSING_HU_EXCHANGE_RATE",
            "INVALID_HU_EXCHANGE_RATE",
            "INVALID_HU_INVOICE_APPEARANCE",
            "UNSUPPORTED_VALUE",
            "MISSING_RO_SUPPLIER_COUNTY_CODE",
            "MISSING_RO_CUSTOMER_COUNTY_CODE",
            "UNKNOWN_ES_CUSTOMER_ID_TYPE",
            "INVALID_ES_NO_RECIPIENT_IDENTIFICATION",
            "INVALID_ES_NUM_REGISTRO_ACUERDO_FACTURACION",
            "INVALID_ES_FACTURAS_RECTIFICADAS_SHAPE",
            "INVALID_ES_IMPORTE_RECTIFICACION_SHAPE",
            "IMPORTE_RECTIFICACION_NOT_ALLOWED_FOR_INCREMENTAL",
            "IMPORTE_RECTIFICACION_REQUIRED_FOR_SUSTITUTIVA",
            "INVALID_PT_STAMP_DUTY_AMOUNT",
            "INVALID_IT_WITHHOLDING_DETAILS",
            "UNKNOWN_JO_INVOICE_TYPE",
            "UNKNOWN_JO_PAYMENT_METHOD",
            "UNKNOWN_JO_CUSTOMER_ID_TYPE",
            "DOCUMENT_TYPE_NOT_SUPPORTED_FOR_COUNTRY",
            "MISSING_JO_CREDIT_NOTE_INVOICE_NUMBER",
            "MISSING_JO_ORIGINAL_INVOICE_UUID",
            "MISSING_JO_ORIGINAL_FULL_AMOUNT",
            "TOO_MANY_METADATA_ENTRIES",
            "METADATA_KEY_TOO_LONG",
            "METADATA_VALUE_TOO_LONG",
            "TOO_MANY_CUSTOM_FIELDS",
            "ORDER_REFERENCE_TOO_LONG",
            "MANDATE_REFERENCE_TOO_LONG",
            "INVALID_DEBITED_IBAN",
            "MISSING_CUSTOMER_NAME",
            "MISSING_CUSTOMER_ADDRESS_CITY",
            "MISSING_CUSTOMER_ADDRESS_COUNTRY"
          ],
          "registry": [
            "MISSING_SELLER_TAX_ID",
            "MISSING_BILLING_ADDRESS",
            "INVALID_CUSTOMER_TAX_ID",
            "MISSING_COUNTRY_CREDENTIALS",
            "MISSING_TAXPAYER_ACTIVITY_CODE",
            "TAX_ID_ADJUSTED",
            "GROSS_AMOUNT_DERIVED",
            "DUPLICATE_INVOICE_NUMBER",
            "CREDIT_NOTE_EXCEEDS_ORIGINAL",
            "REVERSE_CHARGE_MISSING_CUSTOMER_VATID",
            "EXEMPT_LINE_NONZERO_RATE",
            "PEPPOL_TAXABLE_LINE_ZERO_RATE",
            "HU_ISSUE_DATE_TOO_EARLY",
            "JURISDICTION_NOT_ENABLED",
            "CUOTA_ARITHMETIC_MISMATCH",
            "IMPORTE_TOTAL_ARITHMETIC_MISMATCH",
            "1134",
            "1135",
            "IDOTRO_CODIGO_PAIS_REQUIRED",
            "IDOTRO_07_REQUIRES_ES",
            "4111",
            "4122",
            "REGIME_02_REQUIRES_EXEMPT",
            "E5_REQUIRES_GOODS",
            "1176",
            "1270",
            "1287",
            "DEDUCTIBLE_VAT_AMOUNT_OUT_OF_BOUNDS",
            "NEGATIVE_OR_ZERO_INVOICE_AMOUNT",
            "MISSING_HU_SUPPLIER_TAX_NUMBER",
            "MISSING_HU_SUPPLIER_ADDRESS",
            "INVALID_HU_CUSTOMER_TAX_NUMBER",
            "MISSING_HU_CUSTOMER_ADDRESS",
            "MISSING_LEITWEG_ID",
            "MISSING_CUSTOMER_ELECTRONIC_ADDRESS",
            "MISSING_SUPPLIER_ELECTRONIC_ADDRESS",
            "MISSING_PAYMENT_DUE_DATE_OR_TERMS",
            "MISSING_DE_XRECHNUNG_SELLER_CONTACT",
            "DE_XRECHNUNG_BIC_FORBIDDEN",
            "MISSING_DE_COMPANY_REGISTER_DETAILS",
            "MISSING_DE_PEPPOL_SELLER_ADDRESS",
            "MISSING_DE_PEPPOL_CUSTOMER_ADDRESS",
            "MISSING_DE_MANAGING_DIRECTORS",
            "DE_KLEINUNTERNEHMER_CHARGES_VAT",
            "MISSING_SEPA_MANDATE_REFERENCE",
            "MISSING_SEPA_CREDITOR_OR_DEBITED_ACCOUNT",
            "VF_S1_CUOTA_MISMATCH",
            "VF_S2_ON_SIMPLIFIED_INVOICE",
            "1199",
            "1185",
            "VF_IDOTRO_CODIGO_PAIS",
            "1124",
            "VF_IGIC_RATE_NOT_IN_REFERENCE_SET",
            "INVALID_CUIT",
            "INVALID_CUSTOMER_CUIT"
          ]
        }
      },
      "EntityRuleBase": {
        "type": "object",
        "description": "One entity-scoped validation rule, as returned by GET /v1/rules/{ruleCode} and (with evidence added — see EntityRule) GET /v1/rules. `condition`/`transform` are opaque, engine-defined objects — always a `type` key plus rule-specific fields — not evaluated client-side.",
        "properties": {
          "ruleCode": {
            "type": "string"
          },
          "severity": {
            "type": "string",
            "enum": [
              "ERROR",
              "WARNING",
              "ADJUSTMENT",
              "EXCLUDE"
            ]
          },
          "jurisdiction": {
            "type": "string"
          },
          "fieldPath": {
            "type": "string"
          },
          "enabled": {
            "type": "boolean"
          },
          "category": {
            "type": "string"
          },
          "errorCode": {
            "type": "string"
          },
          "suggestedAction": {
            "type": "string"
          },
          "condition": {
            "type": "object",
            "additionalProperties": true
          },
          "transform": {
            "type": "object",
            "additionalProperties": true,
            "nullable": true,
            "description": "Non-null only for ADJUSTMENT-severity rules."
          },
          "version": {
            "type": "integer"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "effectiveFrom": {
            "type": "string",
            "format": "date-time",
            "description": "When this rule's current state took effect. A one-click deactivation is prospective-only, so this is always \"as of when\", never a claim about past evaluations — always equal to updatedAt."
          }
        },
        "required": [
          "ruleCode",
          "severity",
          "jurisdiction",
          "fieldPath",
          "enabled",
          "category",
          "errorCode",
          "suggestedAction",
          "condition",
          "transform",
          "version",
          "createdAt",
          "updatedAt",
          "effectiveFrom"
        ]
      },
      "EntityRule": {
        "allOf": [
          {
            "$ref": "#/components/schemas/EntityRuleBase"
          },
          {
            "type": "object",
            "properties": {
              "appliedCount": {
                "type": "integer",
                "description": "How many times this rule has actually fired for this entity (ADJUSTMENT: correction count; EXCLUDE: exclusion count)."
              },
              "lastAppliedAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              }
            },
            "required": [
              "appliedCount",
              "lastAppliedAt"
            ]
          }
        ]
      },
      "InvoiceRejectedResponse": {
        "type": "object",
        "description": "POST /v1/send's HTTP 200 body when the document is stored with a rejection or a hold that needs action: one or more ERROR-severity checks failed, or a country generator needs more information (`clearanceStatus` `REJECTED`, `NEEDS_INFO` or `SETUP_NEEDED`; `HELD_PLATFORM_FAULT` for a fault on Clearvo's side). Collect-all — every failed check is listed in `errors`, not just the first. `error` names the single highest-priority failure (same shape GET /v1/status's single-error display uses). The status code is 200 because the document is stored: read `clearanceStatus`, `outcome` and `nextStep`, not the HTTP code.",
        "properties": {
          "ok": {
            "type": "boolean",
            "enum": [
              false
            ],
            "description": "Always `false` here: `REJECTED`, `NEEDS_INFO`, `SETUP_NEEDED` and `HELD_PLATFORM_FAULT` are the statuses with `ok: false`, on the first answer and on every replay. Not the outcome: branch on `outcome` / `clearanceStatus`."
          },
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "example": "MISSING_SELLER_TAX_ID"
              },
              "message": {
                "type": "string"
              },
              "isMerchantResolvable": {
                "type": "boolean"
              },
              "fixUrl": {
                "type": "string",
                "nullable": true
              },
              "retryable": {
                "type": "boolean",
                "description": "Whether sending the same document again can succeed without a change."
              },
              "field": {
                "type": "string",
                "description": "The field to correct, when the failure is about a single field."
              }
            }
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ValidationCheckResult"
            },
            "description": "Every failed ERROR-severity check, priority-ordered — errors[0] is always the same failure `error` above names."
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ValidationCheckResult"
            },
            "description": "Non-blocking WARNING-severity checks that also matched. Never the reason for this rejection on its own."
          },
          "adjustments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Adjustment"
            },
            "description": "Auto-corrections already applied before the blocking errors were evaluated — can be non-empty even on a rejection when an unrelated check blocks submission alongside an already-applied, already-audited correction on a different field. Empty when rejectInsteadOfAutoCorrect blocked every would-be correction instead of applying it."
          },
          "rulesetVersion": {
            "type": "integer",
            "description": "Fingerprint of the exact ruleset this request was evaluated against — sum of every applicable registry rule's own version. Matches GET /v1/validations's rulesetVersion for the same country when the registry hasn't changed in between."
          },
          "clearanceStatus": {
            "type": "string",
            "enum": [
              "REJECTED",
              "NEEDS_INFO",
              "SETUP_NEEDED",
              "HELD_PLATFORM_FAULT"
            ],
            "description": "REJECTED for a genuine invoice-level defect. NEEDS_INFO when a field, a setting or a code is missing and you can supply it. SETUP_NEEDED for a merchant configuration gap (e.g. missing authority credentials) with no other blocking failure — resolve on the dashboard, then resubmit. HELD_PLATFORM_FAULT when Clearvo hit a fault on its side: nothing is needed from you and it is resolved automatically. REJECTED wins when both kinds of failure occur on the same request."
          },
          "terminal": {
            "type": "boolean"
          },
          "id": {
            "type": "string",
            "description": "The stored record id. Present on every rejection of a well-formed document (a dry run stores nothing, so it has none). Use it with GET /v1/invoices/{id} and GET /v1/status.",
            "example": "EI-IT-a1b2c3d4"
          },
          "statusClass": {
            "$ref": "#/components/schemas/StatusClass"
          },
          "nextStep": {
            "$ref": "#/components/schemas/NextStep"
          },
          "referenceId": {
            "type": "string",
            "description": "Same record as `id`, under its reference name."
          },
          "outcome": {
            "$ref": "#/components/schemas/SendOutcome"
          },
          "dryRun": {
            "type": "boolean",
            "description": "Echoed back only on a dry run (`dryRun: true`), which answers with the same body and stores nothing; `id` is then absent."
          },
          "details": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            },
            "description": "Present when our own generated document failed its format check (`error.code` `FATTURAPA_SCHEMA_VALIDATION_FAILED`, `FA3_SCHEMA_VALIDATION_FAILED`, `BIS_BILLING_VALIDATION_FAILED` or `PINT_AUNZ_VALIDATION_FAILED`): every violation found."
          }
        },
        "required": [
          "ok",
          "error",
          "errors",
          "clearanceStatus",
          "terminal"
        ]
      },
      "PintAunzViolation": {
        "type": "object",
        "description": "A single failed PINT-AUNZ business rule.",
        "properties": {
          "ruleId": {
            "type": "string",
            "description": "The Schematron rule identifier from the official OpenPeppol PINT-AUNZ specification, e.g. \"ibr-cl-01\" or \"aligned-ibr-001-aunz\".",
            "example": "aligned-ibr-001-aunz"
          },
          "message": {
            "type": "string",
            "description": "The rule's own description text, verbatim from the specification.",
            "example": "An invoice must contain the Seller's ABN (ibt-030) if Seller country (ibt-040) is Australia"
          }
        },
        "required": [
          "ruleId",
          "message"
        ]
      },
      "PintAunzRejectedResponse": {
        "type": "object",
        "description": "POST /v1/send's HTTP 200 body when the generated document fails PINT-AUNZ business-rule validation — country AU or NZ only, evaluated before the AS4 send attempt. The document is stored as `NEEDS_INFO` with `error.code` `PINT_AUNZ_VALIDATION_FAILED`. A distinct, simpler shape from InvoiceRejectedResponse (which covers the shared validation-rules registry, evaluated for every country including AU/NZ, earlier in the request lifecycle) — this check runs afterwards, against the actual generated PINT-AUNZ XML.",
        "properties": {
          "ok": {
            "type": "boolean",
            "enum": [
              false
            ]
          },
          "id": {
            "type": "string",
            "description": "The stored record. Absent on a dry run."
          },
          "clearanceStatus": {
            "type": "string",
            "enum": [
              "NEEDS_INFO"
            ]
          },
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "PINT_AUNZ_VALIDATION_FAILED"
                ]
              },
              "message": {
                "type": "string"
              },
              "fixUrl": {
                "type": "string"
              }
            }
          },
          "details": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PintAunzViolation"
            },
            "description": "Every failed rule, not just the first."
          },
          "hint": {
            "type": "string"
          }
        },
        "required": [
          "ok",
          "error",
          "details"
        ]
      },
      "InvoiceRequest": {
        "type": "object",
        "description": "Documents the fields most integrators need. Additional EN16931-aligned optional fields (delivery, attachments, metadata, customFields, invoicePeriod, payee, buyerReference/orderReference/contractReference/projectReference, note) are accepted but not yet exhaustively documented here. A request that still carries the old `buyer` field name is stored as `REJECTED` (HTTP 200) with one `errors[]` entry per retired key found, code `UNKNOWN_FIELD_BUYER_RENAMED`, each naming `customer` as the field to use instead — there is no silent alias. The same hard cut applies to the retired tax-type-specific line names: `lines[i].vatRate` / `lines[i].vatAmount` (and the same names on `shipping` and `allowanceCharges[]`) are stored as `REJECTED` with `UNKNOWN_FIELD_VAT_RENAMED`, naming `taxRate` / `taxAmount` as the replacement; the retired line names `discount`, `unit`, `itemCode` and `exemption` are stored as `REJECTED` with `UNKNOWN_FIELD_LINE_RENAMED`, naming `discountPercent`/`discountAmount`, `unitOfMeasure`, `sellerItemId`, and `taxTreatment`/`clientTaxCode`. The retired payment names (`payment.method|iban|bic|reference|mandateReference|creditorId|debitedIban`, a string `payment.terms`, `payment.skonto.*`, `payment.dueDate`) are stored as `REJECTED` with `UNKNOWN_FIELD_RENAMED`, naming `payment.means[].<name>`, `payment.terms.text`, `payment.terms.earlyPayment.days|percent|baseAmount` and the top-level `dueDate`. Same `errors[]` shape, one entry per retired key.",
        "required": [
          "documentType",
          "invoiceNumber",
          "issueDate",
          "currency",
          "country",
          "customer",
          "lines"
        ],
        "properties": {
          "companyCode": {
            "type": "string",
            "maxLength": 50,
            "description": "Optional company code (a division of the entity; Enterprise plans only). Trimmed, max 50, matched without regard to case against the entity's active codes (GET /company-codes). Omitted: the entity's default company code, else none. A requested code that is unknown or archived is a stored REJECTED COMPANY_CODE_UNKNOWN rule outcome, and one outside its validFrom/validTo window on the invoice's tax point date (else issue date) is COMPANY_CODE_NOT_VALID_ON_DATE, both stored as a rejection in the InvoiceRejectedResponse envelope (HTTP 200); an entity default outside its window is simply not applied (companyCodeSource 'none' plus a warning); a plan without company codes is HTTP 403 company_codes_not_included. Company-code-scoped rules fire only for the resolved code. Stored on the invoice record."
          },
          "documentType": {
            "type": "string",
            "enum": [
              "invoice",
              "credit_note",
              "debit_note"
            ],
            "example": "invoice",
            "description": "Credit and debit notes need originalInvoiceRef (the original invoice's own invoiceNumber and issueDate). Over Peppol, credit notes and debit notes are supported in Belgium, the Netherlands, Austria, Croatia, Slovakia, Ireland, Norway, Sweden, Denmark, Finland, Lithuania, Latvia, Estonia, Luxembourg, Slovenia, Iceland, Switzerland, Germany (when the Peppol format is used), Australia and New Zealand. A credit note reverses a previously accepted invoice in full; partial credit notes over Peppol are not supported yet (only France and Spain support partial credit notes). A debit note is sent as an invoice marked as a debit note (UNTDID 1001 type code 383), because Peppol has no separate debit-note document. Singapore, Japan and the UAE do not yet support credit or debit notes over Peppol: the document is stored as `REJECTED` with `CREDIT_NOTE_PEPPOL_UNSUPPORTED`; send a corrected invoice instead or contact support."
          },
          "invoiceNumber": {
            "type": "string",
            "description": "Seller-assigned invoice number, unique per seller. The invoice number (with documentType and country, and for a purchase the supplier tax ID and issue date) is what identifies the document: it builds the system identity key that deduplicates, so a changed re-send of the same number is refused 409 IDEMPOTENT_BODY_MISMATCH. A document missing one of those fields is stored under an identity Clearvo mints (your `x-idempotency-key` is used instead when you send one), and the response carries the warning NO_IDENTITY_NO_DEDUP.",
            "example": "INV-2024-001"
          },
          "issueDate": {
            "type": "string",
            "format": "date",
            "example": "2024-01-15"
          },
          "taxPointDate": {
            "type": "string",
            "format": "date",
            "description": "VAT point / tax point date (EN16931 BT-7), when it differs from issueDate. The tax point drives all reporting-side date logic: Compliance Mandate effective-date matching and reporting-period membership both use taxPointDate first, falling back to issueDate. Omit when the two are the same.",
            "example": "2024-01-12"
          },
          "dueDate": {
            "type": "string",
            "format": "date",
            "example": "2024-02-14"
          },
          "currency": {
            "type": "string",
            "minLength": 3,
            "maxLength": 3,
            "example": "EUR"
          },
          "taxReportingCurrency": {
            "type": "string",
            "minLength": 3,
            "maxLength": 3,
            "description": "ISO 4217 currency this invoice must be VAT-reported in, when it differs from `currency` (EN16931 BT-6 — e.g. a EUR invoice for a transaction whose jurisdiction reports in RON). Omit to let it resolve automatically from the transaction's own tax jurisdiction; only set it to override that. When the resolved reporting currency differs from `currency`, you must also supply `exchangeRate` or `taxReporting`, or the call fails with `TAX_REPORTING_CONVERSION_MISSING` — Clearvo never fetches or computes an exchange rate itself.",
            "example": "RON"
          },
          "exchangeRate": {
            "type": "number",
            "description": "`currency` -> `taxReportingCurrency` rate, applied uniformly to every total (subtotal, tax-exclusive amount, tax total, grand total). Mutually exclusive with `taxReporting` — rejected with `EXCHANGE_RATE_AND_TAX_REPORTING_BOTH_SUPPLIED` if both are sent."
          },
          "taxReporting": {
            "type": "object",
            "description": "Authoritative tax-exclusive amount, tax total and total in `taxReportingCurrency`, taken as-is rather than back-derived from a single collapsed figure or exchange rate — your own rounding of net+tax often isn't identical to rounding the gross directly, and the tax amount specifically usually needs to reconcile exactly with your own books. Mutually exclusive with `exchangeRate`.",
            "properties": {
              "taxExclusiveAmount": {
                "type": "number",
                "description": "Required."
              },
              "taxTotal": {
                "type": "number",
                "description": "Required."
              },
              "total": {
                "type": "number",
                "description": "Optional — computed as `taxExclusiveAmount + taxTotal` when omitted."
              }
            },
            "required": [
              "taxExclusiveAmount",
              "taxTotal"
            ]
          },
          "country": {
            "type": "string",
            "minLength": 2,
            "maxLength": 2,
            "description": "Target clearance country — ISO 3166-1 alpha-2 (determines the authority and XML format). This is also the tax jurisdiction the line resolution reports against; for an outgoing invoice the supplier tax ID is taken from the entity's registration in this country (see `supplier.taxId`), so an entity registered here but based elsewhere can omit supplier details.",
            "example": "IT"
          },
          "taxIncluded": {
            "type": "boolean",
            "default": false,
            "description": "When true, all line unitPrices are gross (VAT-inclusive) by default. Can be overridden per line."
          },
          "supplier": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Party"
              }
            ],
            "description": "Seller details. Optional — when omitted the API derives the supplier from the entity's master data (name, tax ID, and address). Provide explicitly to override name/address. `supplier.taxId` is optional either way — see the Party schema: when omitted the entity's registered tax ID for the resolved mandate's country is applied; when supplied it must match that registration (rejected with `SUPPLIER_TAX_ID_MISMATCH`). For Spain SII / VeriFactu a missing or lapsed Spanish registration as of the document date holds the submission as NEEDS_INFO with errorCode MISSING_SUPPLIER_REGISTRATION (200, non-terminal, `fixUrl` to the Registrations page) — add or renew the ES registration, then resubmit with `correctsInvoiceId` set to the held invoice's id. The document is stored as `NEEDS_INFO` with `MISSING_SUPPLIER_DATA` if omitted and the entity's own profile is missing a name, country, or city. For Peppol-routed countries, `supplier.electronicAddress` is never read from this object even if provided — see the Party schema's own field descriptions. On a self-billed invoice (countrySpecific.peppol.selfBilling: true), `supplier` identifies the real third-party seller instead — required (never auto-derived from the entity's own profile; omitting it with no resolvable `supplierRef` is rejected with `MISSING_SELF_BILLING_SUPPLIER`), its taxId is compared/derived against nothing (SUPPLIER_TAX_ID_MISMATCH does not apply — see `customer` below), and its `electronicAddress` IS read from this object (the real seller's own Peppol identity), unlike the ordinary-sale case."
          },
          "customer": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Party"
              }
            ],
            "description": "Counterparty details. On the default `transactionDirection: 'sale'` this is the customer, and required. On `transactionDirection: 'purchase'` (Spain SII received book only) `customer` is the entity's OWN identity, not the vendor (the vendor is `supplier`) — optional, and when omitted it's auto-derived from the entity's master data the same way `supplier` is auto-derived on a sale, resolving the entity's registration for the document's own reporting country (not necessarily its home country). A supplied `customer.taxId` on a purchase must match that registration (rejected with `CUSTOMER_TAX_ID_MISMATCH`); omit it to have the registered value applied automatically. An incomplete entity profile (missing name/country/city) no longer rejects immediately when `customer` is omitted on a purchase or self-billed invoice — the request proceeds through mandate resolution, and is rejected only afterward (rejected with `MISSING_CUSTOMER_NAME` / MISSING_CUSTOMER_ADDRESS_CITY) if the resolved regime actually requires customer identity; a regime that doesn't (e.g. France's e-reporting) accepts it as submitted. On a `transactionDirection: 'purchase'` Spain SII received-book document, a missing or lapsed Spanish registration for this entity (as of the document date) holds the submission as NEEDS_INFO with errorCode MISSING_CUSTOMER_REGISTRATION (the purchase-direction counterpart of `supplier`'s MISSING_SUPPLIER_REGISTRATION) rather than rejecting it — add or renew the ES registration, then resubmit with `correctsInvoiceId` set to the held invoice's id. A request that still carries the old `buyer` field name is stored as `REJECTED` with `UNKNOWN_FIELD_BUYER_RENAMED` in `errors[]`, naming `customer` — there is no silent alias. A self-billed invoice (countrySpecific.peppol.selfBilling: true) shares this same purchase-direction shape even on the default `transactionDirection: 'sale'`: `customer` is still the entity's OWN identity (the buyer being self-billed for), auto-derived and tax-ID-compared/derived exactly as described above for `purchase`, and its `electronicAddress` (BT-49) is backfilled from the entity's own confirmed Peppol Participant ID when omitted."
          },
          "customerType": {
            "type": "string",
            "enum": [
              "B2B",
              "B2C"
            ],
            "description": "Optional customer classification, business (B2B) vs. consumer (B2C), for the whole invoice. Additive: omit it entirely and nothing changes. When provided, it feeds a single WARNING-only cross-check (CUSTOMER_TYPE_TAX_ID_MISMATCH) against customer.taxId's presence/format — never blocks submission. Can be overridden per line via lines[].customerType."
          },
          "lines": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LineItem"
            },
            "minItems": 1
          },
          "allowances": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AllowanceCharge"
            },
            "description": "Document-level allowances (discounts applied to the invoice total): BG-20."
          },
          "charges": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AllowanceCharge"
            },
            "description": "Document-level charges (surcharges applied to the invoice total): BG-21."
          },
          "shipping": {
            "$ref": "#/components/schemas/Shipping"
          },
          "payment": {
            "$ref": "#/components/schemas/Payment"
          },
          "clientTaxCode": {
            "type": "string",
            "description": "RECOMMENDED. Your own ERP tax code (e.g. a SAP two-digit code), configured in advance via POST /tax/client-codes, applied for the WHOLE invoice — the header-level counterpart to `lines[].clientTaxCode`. Resolved onto every line that supplies neither its own `taxRate`/`taxTreatment` nor its own `lines[].clientTaxCode`; a line's own value always wins over this header one. Real ERPs typically assign one tax code per invoice/document rather than per line, so this field lets an integrator send that single code once instead of repeating it on every line. An unrecognised code for this entity does not reject the invoice — it is accepted and held (`clearanceStatus: HELD_UNMAPPED_TAX_CODE`) with a machine-readable reason, same as an unrecognised `lines[].clientTaxCode`.",
            "example": "A1"
          },
          "dryRun": {
            "type": "boolean",
            "default": false,
            "description": "Preview the resolved per-line tax decision without creating a record, generating XML, or enqueueing anything to an authority. The response echoes `dryRun: true` plus `lines[]` (see InvoiceResponse) showing exactly how each line would resolve — including a would-be HELD_UNMAPPED_TAX_CODE outcome — so an integrator can validate a client tax code / taxTreatment setup before really submitting. Never persists an einvoicing_records row. For a German invoice that resolves to the PEPPOL format, `dryRun: true` also runs the real enrichment, the German Peppol content rules (MISSING_DE_PEPPOL_*), the generated-document validation and the customer reachability check, and returns the same `errors[]` / `warnings[]` (or PEPPOL_CUSTOMER_UNREACHABLE) a real send would, plus the `delivery` block — still without storing a record or queueing anything."
          },
          "sandboxSimulateOutcome": {
            "type": "string",
            "enum": [
              "REJECTED"
            ],
            "description": "Sandbox only (`csk_test_*` keys) — ignored for a live key. Forces the submission through the same rejection-handling path a genuine authority rejection would take, using a country-representative rejection code so the shape (fieldPath, message, suggestedAction) matches what a real merchant would see: IT '00404' (customer.taxId not registered with SDI), PL 'NIP_MISMATCH' (seller NIP mismatch), RO 'INVALID_COUNTY_CODE' (missing ISO 3166-2:RO county code), HU 'INVALID_TAX_NUMBER' (customer tax number rejected by NAV), or 'SANDBOX_SIMULATED_REJECTION' for every other country (ES/PT/GR/AR/Peppol and any generate-only country, none of which reject synchronously in real life either). Lets an integration test its own rejection-handling code path without waiting for or provoking a real authority rejection."
          },
          "prepaidAmount": {
            "type": "number",
            "description": "Amount already paid (deposit, down-payment, advance invoice, BT-113). Without `statedPayableAmount`, payableAmount = total − prepaidAmount. Defaults to 0."
          },
          "statedPayableAmount": {
            "type": "number",
            "minimum": 0,
            "description": "Optional: the amount due for payment your own system (ERP/POS) computed. It is authoritative: it is stored as the invoice's payable amount (BT-115) exactly as stated, and the difference from the total of the lines less `prepaidAmount` is stored as the rounding amount (BT-114), whatever its size. A difference of more than one minor unit of `currency` raises the warning ROUNDING_RESIDUAL_OVER_TOLERANCE and no longer rejects or blocks the invoice. A format that cannot carry the stated figure consistently (EN 16931 BR-CO-16) holds the invoice at generation with `PAYABLE_AMOUNT_BR_CO_16` instead of changing it. Omit the field and the payable amount is the total less `prepaidAmount`. The v1 invoice view always carries `totals.payableAmount`; credit and debit notes use positive magnitudes."
          },
          "originalInvoiceRef": {
            "type": "object",
            "description": "Required for documentType credit_note/debit_note: reference to the original invoice by its own seller-assigned number and issue date (the credit or debit note keeps its own new invoiceNumber). If the original is not stored in Clearvo (for example it was issued before you started sending), a credit note is not refused: it is processed on its own, with its own lines and tax, and the response carries the warning CREDIT_NOTE_ORIGINAL_NOT_FOUND.",
            "properties": {
              "invoiceNumber": {
                "type": "string"
              },
              "issueDate": {
                "type": "string",
                "format": "date"
              },
              "ksefNumber": {
                "type": "string",
                "description": "Poland only: the KSeF number of the original invoice (NrFaKorygowanej). Required for a credit note whose original is not stored in Clearvo (CREDIT_NOTE_MISSING_KSEF_NUMBER otherwise); for a stored original Clearvo uses the number it holds."
              }
            }
          },
          "correctsInvoiceId": {
            "type": "string",
            "description": "Clearvo's own id (the `id` returned by a prior /v1/send call) of the invoice this submission corrects — either a fiscal credit_note/debit_note reversing it, or a plain invoice resubmission re-attempting it. Must belong to the same entity and country. Credit and debit notes should use originalInvoiceRef (the original invoice's own number and issue date) instead; this field remains for resubmitting a rejected invoice."
          },
          "customerReference": {
            "type": "string",
            "description": "Internal back-office reference (e.g. SAP BELNR). Stored but never sent to the authority."
          },
          "countrySpecific": {
            "type": "object",
            "description": "Optional country-level extensions. Fields vary by country. `peppol.selfBilling` (boolean, default false) marks this as a self-billing document (customer issues on the seller's behalf), switching to a self-billing CustomizationID/ProfileID and UNTDID 1001 type codes (389 for invoices, 261 for credit notes) instead of the regular billing ones. AU and NZ use their own PINT-AUNZ self-billing profile; every Peppol country except SG and JP uses the generic EU BIS Self-Billing 3.0 profile. Silently ignored for SG and JP, whose self-billing document-type registration is not yet supported. Company-law disclosure facts (DE Handelsregister details and managing directors, FR Code de commerce Art. R123-237 capital/RCS city, IT Codice Civile Art. 2250 Registro Imprese/REA/capital/sole-shareholder notice, PT Código das Sociedades Comerciais Art. 171 registry office/capital, ES Código de Comercio Art. 16/19-21 Registro Mercantil tomo/folio/hoja/capital/liquidation notice) are entity-level facts, normally set once — `legalForm` via PATCH /entities/{id}'s entityFacts (see GET /entities/fact-definitions), the country-specific ones on the entity's registration extraFields — and rendered automatically (BT-30 legalRegistrationId, BT-33 additionalLegalInfo) on every invoice the entity issues, keyed on the entity's own established country rather than which country's clearance rules a given invoice itself resolves to. A value sent for the same field in the matching `countrySpecific.<cc>` block overrides the stored one for that invoice only (this includes `de.legalForm`; for IT, ES, PT and FR the legal form comes from entityFacts only). `es.duaNumber` (Spain SII block 3): NumeroDUA (Documento Único Administrativo) — required, and only meaningful, when a `transactionDirection: 'purchase'` line's tipoFactura resolves to F5 (import). Missing on an import produces `NEEDS_INFO` naming `countrySpecific.es.duaNumber`. Spain VeriFactu and SII (`es`): `es.tipoFactura` (explicit F1/F2/R1-R5 override; under VeriFactu an invoice with no buyer identification, an original document and a total up to EUR 400 is issued as F2 automatically, a credit note against an F2 as R5), `es.claveRegimen` (optional override, derived per line by default; must be valid for the line's own tax), `es.customerNif`, `es.verifactuMode`, `es.calificacionOperacion` (S1/S2/N1/N2), `es.tipoRectificativa` ('S'/'I'), `es.facturasRectificadas`, `es.importeRectificacion`, `es.issuedByThirdParty`, and the VeriFactu buyer and simplified-invoice fields: `es.customerIdType` ('passport', 'residence_document', 'residence_certificate', 'other' or 'not_registered' - what kind of document `customer.taxId` is when it is not a Spanish NIF or EU VAT number; send the buyer's country in `customer.establishmentCountry`; an unrecognised identifier without it holds the invoice as `NEEDS_INFO` with errorCode `MANDATE_INCOMPLETE_DATA`, and `missingFields` names `customerIdType`), `es.noRecipientIdentification` (boolean - your declaration that the invoice is lawfully issued without identifying the recipient, RD 1619/2012 art. 6.1.d; simplified invoices only, never inferred, AEAT 1185), and `es.numRegistroAcuerdoFacturacion` (billing agreement number, at most 15 characters). A simplified invoice above EUR 3,000 needs one of the last two or it is held as `NEEDS_INFO` (AEAT 1150). `customer.name` is optional on an F2/R5 invoice. Canary Islands: Clearvo registers your Canary Islands invoices with the AEAT VeriFactu system under the same Spanish tax number, reporting IGIC as Impuesto 03 in the same record chain as VAT. Set the `es_tax_territory` field (`mainland`, `canary_islands` or `both`) on your Spanish registration (PATCH /v1/tax/registrations/{id} extraFields; see GET /v1/tax/registrations/field-definitions) - an IGIC line without it returns `VERIFACTU_TAX_TERRITORY_REQUIRED`. Clearvo does not file your IGIC returns or the Canary SII with the Agencia Tributaria Canaria, and does not support IPSI (`VERIFACTU_IPSI_UNSUPPORTED`); sales between the Canary Islands and the mainland return `VERIFACTU_CROSS_TERRITORY_UNSUPPORTED`, and so does a credit note whose tax (IVA or IGIC) differs from the invoice it rectifies. An exempt line reports the exemption cause stated on its client tax code (E1 to E6; E1 to E8 for IGIC), defaulting to E1 on a mainland exempt line when none is stated; an IGIC exempt line with no stated cause is held. A credit note is sent with positive amounts and reported to AEAT with negative ones (rectificativa por diferencias). VeriFactu applies from 2027-01-01 for corporate-tax payers and 2027-07-01 for everyone else; Clearvo registers every Spanish invoice dated on or after 2027-01-01, and sandbox invoices must carry such a date (the sandbox never contacts the AEAT). Codes: `VERIFACTU_CUSTOMER_ID_UNRESOLVED`, `VERIFACTU_CUSTOMER_NAME_MISSING`, `VERIFACTU_TAX_TERRITORY_REQUIRED`, `VERIFACTU_CROSS_TERRITORY_UNSUPPORTED`, `VERIFACTU_IPSI_UNSUPPORTED`, `VERIFACTU_DESGLOSE_UNMAPPED_DECISION`, `VERIFACTU_EXEMPTION_CODE_INVALID`, `VERIFACTU_CLAVE_REGIMEN_INVALID`, `VERIFACTU_DESGLOSE_TOO_MANY_LINES`, `VERIFACTU_NUM_SERIE_INVALID`, `VERIFACTU_TIPO_FACTURA_MISMATCH` (rejected: `countrySpecific.es.tipoFactura` must be R1-R5 on a credit note and F1-F3 on an invoice), `VERIFACTU_CURRENCY_UNSUPPORTED` (rejected: a Spanish VeriFactu invoice must be in EUR; AEAT records amounts in euros with no currency field), rejected with `UNKNOWN_ES_CUSTOMER_ID_TYPE`, `INVALID_ES_NO_RECIPIENT_IDENTIFICATION`, `INVALID_ES_NUM_REGISTRO_ACUERDO_FACTURACION`, and the mandate validation rules `ES_VERIFACTU_S1_CUOTA`, `ES_VERIFACTU_S2_NOT_SIMPLIFIED`, `ES_VERIFACTU_CLAVE_01_NO_E2_E3`, `ES_VERIFACTU_ART61D_ONLY_SIMPLIFIED`, `ES_VERIFACTU_IDOTRO_CODIGO_PAIS`, `ES_VERIFACTU_IVA_RATE_SET` and the warning `ES_VERIFACTU_IGIC_RATE_REFERENCE` (an IGIC rate outside the usual set; the invoice is still sent as provided). `de.invoiceFormat` ('ZUGFERD', 'XRECHNUNG' or 'PEPPOL', highest-precedence override — see the top-level `documentFormat`/`formatResolvedFrom` response fields and PATCH /customers/{id}'s own countrySpecific.de.invoiceFormat for the per-customer default) and `de.leitwegId` (BT-10 — XRECHNUNG only, falls back to the request's own top-level buyerReference, then to the LEITWEG_ID reference stored on the customer) let a single POST /send choose Germany's invoice format explicitly. 'PEPPOL' sends the invoice to the customer over the Peppol network as a Peppol BIS Billing 3.0 invoice (documentFormat UBL_PEPPOL_BIS): it needs the entity's confirmed Peppol Participant ID (rejected with `MISSING_SELLER_PEPPOL_ID` otherwise) and an explicit Peppol ID for the customer (`customer.electronicAddress` with `schemeId` + `value`, or the stored customer's human-confirmed PEPPOL_PARTICIPANT_ID reference) — a German customer's Peppol ID is never derived from a VAT number. For a German XRECHNUNG or PEPPOL document the customer's electronic address (BT-49) is the first available of: the explicit `customer.electronicAddress` (`value` + `schemeId`), the stored confirmed Peppol participant ID, the Leitweg-ID (scheme 0204; it must pass the Leitweg-ID check-digit validation, so a free-text buyerReference or purchase-order number is ignored), the customer's contact email (scheme EM), the customer's German VAT ID (scheme 9930, DE plus 9 digits). The supplier's electronic address (BT-34) is the first of: the entity's confirmed Peppol participant ID, its contact email (EM), its German VAT ID (9930). When none exists the send is blocked before any XML is generated with ERROR `MISSING_CUSTOMER_ELECTRONIC_ADDRESS` or `MISSING_SUPPLIER_ELECTRONIC_ADDRESS`. A customer that cannot be reached is stored as `NEEDS_INFO` with `PEPPOL_CUSTOMER_UNREACHABLE` (see PeppolCustomerUnreachableResponse), never a silent fallback to another format. Credit and debit notes cannot be sent over Peppol for Germany yet (rejected with `CREDIT_NOTE_PEPPOL_UNSUPPORTED`), and neither can a self-billed document (`peppol.selfBilling`): requesting PEPPOL on one is a 400 DE_SELF_BILLED_PEPPOL_NOT_SUPPORTED, and a stored customer/entity default of PEPPOL on a self-billed document is rejected with `DE_SELF_BILLED_FORMAT_CHOICE_REQUIRED` (pick ZUGFERD or XRECHNUNG). Use `dryRun: true` to check all of this without storing anything. `ar.condicionIVAReceptorId` (ARCA's real FEParamGetCondicionIvaReceptor code, RG (ARCA) 5616/2024 — see the Customer schema's own countrySpecific.ar.condicionIVAReceptorId for the full code list; default 5, Consumidor Final) and `ar.cbteTipo` (override for the computed comprobante type, normally derived from condicionIVAReceptorId) — a foreign-customer condicionIVAReceptorId (8 Proveedor del Exterior / 9 Cliente del Exterior) with no explicit `ar.cbteTipo` override is rejected with `AR_FOREIGN_CUSTOMER_UNSUPPORTED` naming `countrySpecific.ar.condicionIVAReceptorId` (Factura E, comprobante 19, isn't generated automatically yet). `it.esigibilitaIva` ('I' default / 'D' / 'S' — DatiRiepilogo/EsigibilitaIVA), `it.withholding` (boolean) + `it.withholdingDetails` (`{ type: 'RT01'..'RT06', rate, amount, paymentReason }`, required once withholding is true or a rejection with `IT_WITHHOLDING_DETAILS_MISSING`; a supplied-but-malformed type/rate/amount/paymentReason is rejected with `IT_WITHHOLDING_DETAILS_INVALID` naming the specific offending sub-field), `it.stampDuty` (boolean, emits DatiBollo), `it.cigCode`/`it.cupCode` (DatiOrdineAcquisto CIG/CUP, require the request's own `orderReference` or a rejection with `IT_CIG_CUP_MISSING_ORDER_REFERENCE`). `hu.invoiceCategory` ('NORMAL' — the only supported value today) and `hu.cashAccountingIndicator` (boolean, pénzforgalmi cash-basis VAT flag). `pl.gtuCodes` (invoice-level GTU fallback — prefer `lines[].gtuCode`), `pl.cashAccountingIndicator` (boolean, Adnotacje P_16 metoda kasowa), `pl.exemptionLegalBasisType` ('national_act' | 'eu_directive' | 'other', required whenever any line is exempt or a rejection with `PL_EXEMPTION_LEGAL_BASIS_MISSING`). `pt.stampDutyAmount` (Imposto do Selo, caller-supplied, emits AT QR field M and folds into field N, and into the monthly SAF-T file's TaxPayable/GrossTotal too). `ro.supplierTaxRegistered`/`ro.customerTaxRegistered` (boolean; whether that party genuinely holds an active VAT registration (a CUI, \"RO\" prefix) rather than a bare fiscal identifier (CIF) — omit to default to whether the party's `taxId` carried the \"RO\" prefix; an unresolved supplier is rejected with `RO_SUPPLIER_TAX_REGISTRATION_STATUS_UNKNOWN`, an unresolved B2B (or untyped) customer a rejection with `RO_CUSTOMER_TAX_REGISTRATION_STATUS_UNKNOWN`, and a `customerType: 'B2C'` customer defaults to unregistered). `ro.customerSector`/`ro.supplierSector` ('SECTOR1'..'SECTOR6', required whenever the corresponding party's `address.countyCode` resolves to 'RO-B' Bucharest, or a rejection with `RO_BUCHAREST_SECTOR_REQUIRED`); a Romanian `documentType: 'debit_note'` with no `originalInvoiceRef` is rejected with `RO_DEBIT_NOTE_MISSING_ORIGINAL_REF`. A Jordanian `documentType: 'credit_note'` with no `originalInvoiceRef.invoiceNumber` is rejected with `MISSING_JO_CREDIT_NOTE_INVOICE_NUMBER`. Malaysia requires `supplier.taxId` (rejected with `MY_SUPPLIER_TIN_REQUIRED` if absent) and, for a B2B `customer.taxId` (rejected with `MY_CUSTOMER_TIN_REQUIRED` if absent and not self-billing/B2C) — see MyInvois's general-TIN placeholder codes for the B2C/self-billed exceptions. An Egyptian line whose `taxCategory` can't resolve to a known ETA VAT subtype is rejected with `EG_TAX_SUBTYPE_UNRESOLVED` naming `lines[].taxCategory`. Greece additionally holds when a line's optional `lineType` ('product', 'service', 'digital_service', 'transport' or 'other') is explicitly set and genuinely disagrees with the tax code's own resolved goods/service classification (rejected with `GR_LINE_TYPE_SUPPLY_TYPE_MISMATCH` naming `lines[{i}].lineType` — an OMITTED lineType never triggers this, only one you explicitly set and that conflicts), when a mixed-kind invoice has no single header-level invoiceType to describe it (rejected with `GR_MIXED_LINE_TYPE`), or when an exempt/zero-rated/reverse-charge line has no resolvable vatExemptionCategory (rejected with `GR_EXEMPTION_CATEGORY_MISSING` naming `lines[{i}].taxCategory`, with a `/tax-codes` fixUrl). Across every EN16931-shaped country generator (IT/PL/RO/GR/Peppol), an internally inconsistent (tax category, rate) pair on a line or VAT-breakdown entry — never reachable via the documented `/v1/send` request/response fields alone, since resolveInvoiceTaxCodes already holds this upstream — is the shared defense-in-depth rejection `TAX_LINE_INCONSISTENT`.",
            "additionalProperties": true,
            "example": {
              "it": {
                "tipoDocumento": "TD01",
                "regimeFiscale": "RF01"
              },
              "peppol": {
                "selfBilling": true
              },
              "es": {
                "duaNumber": "24ES0012345678901234",
                "customerIdType": "passport",
                "noRecipientIdentification": false
              },
              "de": {
                "invoiceFormat": "XRECHNUNG",
                "leitwegId": "04011000-1234512345-06",
                "kleinunternehmer": false
              },
              "ar": {
                "condicionIVAReceptorId": 5
              },
              "pl": {
                "exemptionLegalBasisType": "eu_directive"
              }
            },
            "properties": {
              "it": {
                "type": "object",
                "description": "Italy (SDI / FatturaPA).",
                "properties": {
                  "progressivoInvio": {
                    "type": "string",
                    "description": "Progressive file number, max 10 alphanumeric characters. Auto-generated when absent.",
                    "maxLength": 10
                  },
                  "tipoDocumento": {
                    "type": "string",
                    "description": "TD01, TD04, TD05 etc. Auto-derived from documentType."
                  },
                  "regimeFiscale": {
                    "type": "string",
                    "description": "Seller's fiscal regime code, e.g. RF01 (ordinary, the default)."
                  },
                  "codiceDestinatario": {
                    "type": "string",
                    "description": "Customer's 7-character SDI routing code; XXXXXXX for non-Italian customers, 0000000 for a PEC or B2C customer.",
                    "minLength": 7,
                    "maxLength": 7
                  },
                  "codiceFiscale": {
                    "type": "string",
                    "description": "Customer's 16-character personal fiscal code — required for a domestic B2C customer with no VAT ID."
                  },
                  "pecDestinatario": {
                    "type": "string",
                    "description": "Customer's PEC (certified email) address — required when codiceDestinatario is 0000000.",
                    "format": "email"
                  },
                  "cigCode": {
                    "type": "string",
                    "description": "CIG tender code (DatiOrdineAcquisto, B2G). Requires the request's orderReference (rejected with `IT_CIG_CUP_MISSING_ORDER_REFERENCE`)."
                  },
                  "cupCode": {
                    "type": "string",
                    "description": "CUP project fund code (DatiOrdineAcquisto, B2G). Requires the request's orderReference."
                  },
                  "withholding": {
                    "type": "boolean",
                    "description": "Ritenuta (withholding tax) flag — requires withholdingDetails, else it is rejected with `IT_WITHHOLDING_DETAILS_MISSING`."
                  },
                  "withholdingDetails": {
                    "type": "object",
                    "description": "DatiRitenuta detail — required whenever withholding is true.",
                    "properties": {
                      "type": {
                        "type": "string",
                        "enum": [
                          "RT01",
                          "RT02",
                          "RT03",
                          "RT04",
                          "RT05",
                          "RT06"
                        ]
                      },
                      "rate": {
                        "type": "number",
                        "description": "Withholding rate."
                      },
                      "amount": {
                        "type": "number",
                        "description": "Withholding amount."
                      },
                      "paymentReason": {
                        "type": "string",
                        "description": "Causale pagamento code."
                      }
                    },
                    "required": [
                      "type",
                      "rate",
                      "amount",
                      "paymentReason"
                    ]
                  },
                  "esigibilitaIva": {
                    "type": "string",
                    "enum": [
                      "I",
                      "D",
                      "S"
                    ],
                    "default": "I",
                    "description": "VAT chargeability: Immediate, Deferred or Split payment (PA)."
                  },
                  "stampDuty": {
                    "type": "boolean",
                    "description": "Emit DatiBollo (virtual stamp duty, EUR 2, for non-VAT invoices above EUR 77.47)."
                  },
                  "registroImpreseOffice": {
                    "type": "string",
                    "description": "Companies Register province office, e.g. Milano. Falls back to the entity's stored value when omitted; a value sent here wins for this invoice only."
                  },
                  "registroImpreseNumber": {
                    "type": "string",
                    "description": "Companies Register registration number (BT-30). Falls back to the entity's stored value when omitted; a value sent here wins for this invoice only."
                  },
                  "reaNumber": {
                    "type": "string",
                    "description": "REA number (optional). Falls back to the entity's stored value when omitted; a value sent here wins for this invoice only."
                  },
                  "capitaleSociale": {
                    "type": "string",
                    "description": "Issued share capital, no currency symbol. Falls back to the entity's stored value when omitted; a value sent here wins for this invoice only."
                  },
                  "socioUnico": {
                    "type": "boolean",
                    "description": "Whether an SpA/Srl currently has a sole shareholder — self-declared, never derived. Falls back to the entity's stored value when omitted; a value sent here wins for this invoice only."
                  }
                }
              },
              "pl": {
                "type": "object",
                "description": "Poland (KSeF FA(3)).",
                "properties": {
                  "sellerNip": {
                    "type": "string",
                    "description": "Overrides supplier.taxId."
                  },
                  "customerNip": {
                    "type": "string",
                    "description": "Overrides customer.taxId for Polish customers."
                  },
                  "rodzajFaktury": {
                    "type": "string",
                    "description": "VAT or KOR. Auto-derived from documentType."
                  },
                  "splitPayment": {
                    "type": "boolean",
                    "description": "MPP split-payment flag — mandatory for Annex 15 goods above PLN 15,000."
                  },
                  "gtuCodes": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Invoice-level GTU_01..GTU_13 fallback — prefer lines[].gtuCode. Used only when it carries exactly one code and no line sets its own."
                  },
                  "cashAccountingIndicator": {
                    "type": "boolean",
                    "description": "Metoda kasowa (cash accounting) status; falls back to the entity's PL registration. Defaults to false."
                  },
                  "exemptionLegalBasisType": {
                    "type": "string",
                    "enum": [
                      "national_act",
                      "eu_directive",
                      "other"
                    ],
                    "description": "Which of P_19A (national_act), P_19B (eu_directive) or P_19C (other) an exempt line's wording is cited under. Required whenever any line is exempt, else it is rejected with `PL_EXEMPTION_LEGAL_BASIS_MISSING`."
                  }
                }
              },
              "es": {
                "type": "object",
                "description": "Spain (SII and VeriFactu). See the prose above for the validation codes.",
                "properties": {
                  "tipoFactura": {
                    "type": "string",
                    "description": "Explicit invoice type override: F1/F2/R1-R5."
                  },
                  "claveRegimen": {
                    "type": "string",
                    "description": "Optional VeriFactu ClaveRegimen override; derived per line by default and must be valid for the line's own tax."
                  },
                  "customerNif": {
                    "type": "string",
                    "description": "Spanish customer NIF. Taken from customer.taxId by default."
                  },
                  "verifactuMode": {
                    "type": "boolean",
                    "description": "true = real-time AEAT submission. Defaults from configuration."
                  },
                  "calificacionOperacion": {
                    "type": "string",
                    "enum": [
                      "S1",
                      "S2",
                      "N1",
                      "N2"
                    ],
                    "description": "Operation classification."
                  },
                  "tipoRectificativa": {
                    "type": "string",
                    "enum": [
                      "S",
                      "I"
                    ],
                    "description": "Rectificativa kind: S (sustitutiva) or I (incremental). Required with an R1-R5 tipoFactura, forbidden otherwise. Defaults to I for a credit_note."
                  },
                  "facturasRectificadas": {
                    "type": "array",
                    "description": "The prior invoice(s) a rectificativa corrects — at least one is required whenever tipoFactura is R1-R5.",
                    "items": {
                      "type": "object",
                      "description": "",
                      "properties": {
                        "numSerieFacturaEmisor": {
                          "type": "string"
                        },
                        "fechaExpedicionFacturaEmisor": {
                          "type": "string",
                          "format": "date"
                        }
                      },
                      "required": [
                        "numSerieFacturaEmisor",
                        "fechaExpedicionFacturaEmisor"
                      ]
                    }
                  },
                  "importeRectificacion": {
                    "type": "object",
                    "description": "Substituted base/quota total — required when tipoRectificativa is S, forbidden when I.",
                    "properties": {
                      "baseRectificada": {
                        "type": "number"
                      },
                      "cuotaRectificada": {
                        "type": "number"
                      }
                    },
                    "required": [
                      "baseRectificada",
                      "cuotaRectificada"
                    ]
                  },
                  "issuedByThirdParty": {
                    "type": "boolean",
                    "description": "Issued-book only: true when a third party (or the recipient under a self-billing agreement) issued the invoice; grants the extended SII deadline."
                  },
                  "duaNumber": {
                    "type": "string",
                    "description": "NumeroDUA — required, and only meaningful, when a purchase's tipoFactura resolves to F5 (import)."
                  },
                  "customerIdType": {
                    "type": "string",
                    "enum": [
                      "passport",
                      "residence_document",
                      "residence_certificate",
                      "other",
                      "not_registered"
                    ],
                    "description": "VeriFactu only: what kind of document customer.taxId is when it is not a Spanish NIF or EU VAT number (AEAT IDType 03-07). Send the buyer's country in customer.establishmentCountry."
                  },
                  "noRecipientIdentification": {
                    "type": "boolean",
                    "description": "VeriFactu only: declaration that the invoice is lawfully issued without identifying the recipient (RD 1619/2012 art. 6.1.d); simplified invoices only, never inferred."
                  },
                  "numRegistroAcuerdoFacturacion": {
                    "type": "string",
                    "description": "VeriFactu only: billing agreement number (max 15 characters) that allows a simplified invoice above EUR 3,000.",
                    "maxLength": 15
                  },
                  "registroMercantilOficina": {
                    "type": "string",
                    "description": "Registro Mercantil province office, e.g. Madrid. Falls back to the entity's stored value when omitted; a value sent here wins for this invoice only."
                  },
                  "tomo": {
                    "type": "string",
                    "description": "Registry tomo. Falls back to the entity's stored value when omitted; a value sent here wins for this invoice only."
                  },
                  "folio": {
                    "type": "string",
                    "description": "Registry folio. Falls back to the entity's stored value when omitted; a value sent here wins for this invoice only."
                  },
                  "hoja": {
                    "type": "string",
                    "description": "Registration sheet code, e.g. M-67890 (BT-30). Falls back to the entity's stored value when omitted; a value sent here wins for this invoice only."
                  },
                  "capitalSocial": {
                    "type": "string",
                    "description": "Subscribed/paid-in share capital, no currency symbol. Falls back to the entity's stored value when omitted; a value sent here wins for this invoice only."
                  },
                  "enLiquidacion": {
                    "type": "boolean",
                    "description": "Whether the company is currently in liquidation (Art. 21) — self-declared, never derived. Falls back to the entity's stored value when omitted; a value sent here wins for this invoice only."
                  }
                }
              },
              "pt": {
                "type": "object",
                "description": "Portugal (AT e-Fatura / SAF-T). The series, ATCUD and software certificate come from the entity's stored AT credentials and cannot be set per request.",
                "properties": {
                  "tipoDocumento": {
                    "type": "string",
                    "description": "FT/FS/ND/NC. Auto-derived from documentType."
                  },
                  "stampDutyAmount": {
                    "type": "number",
                    "description": "Imposto do Selo (stamp duty) amount, caller-supplied. Emitted as AT QR field M and folded into field N."
                  },
                  "conservatoria": {
                    "type": "string",
                    "description": "Commercial registry office, e.g. Lisboa. Falls back to the entity's stored value when omitted; a value sent here wins for this invoice only."
                  },
                  "numeroMatricula": {
                    "type": "string",
                    "description": "Registration (matrícula) number (BT-30). Falls back to the entity's stored value when omitted; a value sent here wins for this invoice only."
                  },
                  "capitalSocial": {
                    "type": "string",
                    "description": "Share capital, no currency symbol. Falls back to the entity's stored value when omitted; a value sent here wins for this invoice only."
                  }
                }
              },
              "fr": {
                "type": "object",
                "description": "France.",
                "properties": {
                  "profile": {
                    "type": "string",
                    "enum": [
                      "EN16931",
                      "BASIC",
                      "MINIMUM",
                      "EXTENDED"
                    ],
                    "default": "EN16931",
                    "description": "Factur-X profile."
                  },
                  "deliveryDate": {
                    "type": "string",
                    "description": "Actual delivery date, YYYY-MM-DD. Falls back to delivery.date.",
                    "format": "date"
                  },
                  "siret": {
                    "type": "string",
                    "description": "Seller SIRET (14 digits), required for Chorus Pro B2G (BT-30). Falls back to the entity's stored SIRET; also populates supplier.legalRegistrationId when that is unset."
                  },
                  "capitalSocial": {
                    "type": "string",
                    "description": "Share capital, e.g. \"10 000\" (Code de commerce Art. R123-237). Falls back to the entity's stored value when omitted; a value sent here wins for this invoice only."
                  },
                  "rcsCity": {
                    "type": "string",
                    "description": "RCS registry-court city, e.g. Paris. Falls back to the entity's stored value when omitted; a value sent here wins for this invoice only."
                  }
                }
              },
              "de": {
                "type": "object",
                "description": "Germany (ZUGFeRD / XRechnung / Peppol).",
                "properties": {
                  "leitwegId": {
                    "type": "string",
                    "description": "Leitweg-ID for German B2G routing (BT-10), format SS-XXXXX-CC. Required for the public sector (BR-DE-15); falls back to the top-level buyerReference."
                  },
                  "invoiceFormat": {
                    "type": "string",
                    "enum": [
                      "ZUGFERD",
                      "XRECHNUNG",
                      "PEPPOL"
                    ],
                    "description": "Per-request override of the German invoice format — the highest-priority level of the resolver; an unrecognized value is rejected with 400 INVALID_DE_INVOICE_FORMAT."
                  },
                  "skontoTerms": {
                    "type": "string",
                    "description": "Structured skonto terms (overrides payment.terms.earlyPayment for XRechnung formatting)."
                  },
                  "steuernummer": {
                    "type": "string",
                    "description": "German Steuernummer (BT-32). Used only when the seller has no VAT ID; falls back to the entity's stored value."
                  },
                  "legalForm": {
                    "type": "string",
                    "description": "Legal form (e.g. GmbH) driving which company-law disclosure applies. Falls back to the entity's entityFacts.legalForm."
                  },
                  "handelsregisternummer": {
                    "type": "string",
                    "description": "Handelsregister number. Falls back to the entity's stored value when omitted; a value sent here wins for this invoice only."
                  },
                  "registergericht": {
                    "type": "string",
                    "description": "Registry court. Falls back to the entity's stored value when omitted; a value sent here wins for this invoice only."
                  },
                  "registeredSeat": {
                    "type": "string",
                    "description": "Registered seat. Falls back to the entity's stored value when omitted; a value sent here wins for this invoice only."
                  },
                  "geschaeftsfuehrer": {
                    "type": "string",
                    "description": "Managing director(s). Falls back to the entity's stored value when omitted; a value sent here wins for this invoice only."
                  },
                  "kleinunternehmer": {
                    "type": "boolean",
                    "description": "Kleinunternehmer (small-business exemption). Falls back to the entity's stored value when omitted; a value sent here wins for this invoice only."
                  }
                }
              },
              "hu": {
                "type": "object",
                "description": "Hungary (NAV Online Számla).",
                "properties": {
                  "invoiceCategory": {
                    "type": "string",
                    "enum": [
                      "NORMAL",
                      "SIMPLIFIED",
                      "AGGREGATE"
                    ],
                    "default": "NORMAL"
                  },
                  "invoiceAppearance": {
                    "type": "string",
                    "enum": [
                      "PAPER",
                      "ELECTRONIC",
                      "EDI",
                      "UNKNOWN"
                    ],
                    "default": "ELECTRONIC"
                  },
                  "exchangeRate": {
                    "type": "number",
                    "description": "Exchange rate to HUF as used on the invoice — required when currency is not HUF; Clearvo reports the rate you supply."
                  },
                  "smallBusinessIndicator": {
                    "type": "boolean",
                    "description": "Kisadózó (KATA flat-rate) taxpayer flag."
                  },
                  "cashAccountingIndicator": {
                    "type": "boolean",
                    "description": "Pénzforgalmi (cash-basis VAT) flag."
                  },
                  "ekaerId": {
                    "type": "string",
                    "description": "EKÁER road transport ID for goods transport."
                  },
                  "invoiceOperation": {
                    "type": "string",
                    "enum": [
                      "CREATE",
                      "MODIFY",
                      "STORNO"
                    ],
                    "description": "Modification-document operation for a credit_note: STORNO = full cancellation, MODIFY = correction (implied when omitted). Carried on the record; not yet consumed by the NAV submission, which currently submits CREATE documents only."
                  },
                  "modificationIndex": {
                    "type": "integer",
                    "description": "Sequential modification index against the original invoice (1, 2, 3...), required by NAV when invoiceOperation is MODIFY or STORNO."
                  }
                }
              },
              "ro": {
                "type": "object",
                "description": "Romania (RO e-Factura).",
                "properties": {
                  "supplierCountyCode": {
                    "type": "string",
                    "description": "Seller county code (ISO 3166-2:RO), inferred from supplier.address.countyCode."
                  },
                  "customerCountyCode": {
                    "type": "string",
                    "description": "Customer county code (ISO 3166-2:RO), inferred from customer.address.countyCode."
                  },
                  "customerSector": {
                    "type": "string",
                    "enum": [
                      "SECTOR1",
                      "SECTOR2",
                      "SECTOR3",
                      "SECTOR4",
                      "SECTOR5",
                      "SECTOR6"
                    ],
                    "description": "Bucharest sector — required when the customer's county is RO-B."
                  },
                  "supplierSector": {
                    "type": "string",
                    "enum": [
                      "SECTOR1",
                      "SECTOR2",
                      "SECTOR3",
                      "SECTOR4",
                      "SECTOR5",
                      "SECTOR6"
                    ],
                    "description": "Bucharest sector of the seller — required when the seller's county is RO-B; defaults from the entity's stored ro_supplier_sector."
                  },
                  "supplierTaxRegistered": {
                    "type": "boolean",
                    "description": "Whether the seller holds an active VAT registration (a CUI, RO prefix) rather than a bare CIF. Defaults to whether supplier.taxId carried the RO prefix."
                  },
                  "customerTaxRegistered": {
                    "type": "boolean",
                    "description": "Same for the customer; unresolved is held unless customerType is B2C."
                  }
                }
              },
              "ar": {
                "type": "object",
                "description": "Argentina (ARCA).",
                "properties": {
                  "cbteTipo": {
                    "type": "integer",
                    "description": "Override for the computed comprobante type (1=A, 6=B, 11=C, 3=NC-A, 8=NC-B, 13=NC-C)."
                  },
                  "puntoDeVenta": {
                    "type": "integer",
                    "description": "Point of sale number."
                  },
                  "condicionIVAReceptorId": {
                    "type": "integer",
                    "enum": [
                      1,
                      4,
                      5,
                      6,
                      7,
                      8,
                      9,
                      10,
                      13,
                      15,
                      16
                    ],
                    "default": 5,
                    "description": "Buyer VAT condition (ARCA FEParamGetCondicionIvaReceptor, RG 5616/2024): 1 Responsable Inscripto, 4 IVA Sujeto Exento, 5 Consumidor Final, 6 Responsable Monotributo, 7 Sujeto No Categorizado, 8 Proveedor del Exterior, 9 Cliente del Exterior, 10 IVA Liberado (Ley 19.640), 13 Monotributista Social, 15 IVA No Alcanzado, 16 Monotributo Trabajador Independiente Promovido."
                  },
                  "condicionIVAIssuer": {
                    "type": "integer",
                    "enum": [
                      1,
                      2,
                      3
                    ],
                    "default": 1,
                    "description": "Issuer's AFIP condition: 1 RI, 2 Monotributista, 3 Exento."
                  },
                  "docTipo": {
                    "type": "integer",
                    "enum": [
                      80,
                      96,
                      99
                    ],
                    "description": "Buyer document type."
                  },
                  "docNro": {
                    "type": "string",
                    "description": "Buyer document number."
                  },
                  "concepto": {
                    "type": "integer",
                    "enum": [
                      1,
                      2,
                      3
                    ],
                    "description": "1 products, 2 services, 3 both."
                  },
                  "fchServDesde": {
                    "type": "string",
                    "description": "Service period start, YYYYMMDD — required for services."
                  },
                  "fchServHasta": {
                    "type": "string",
                    "description": "Service period end, YYYYMMDD — required for services."
                  },
                  "fchVtoPago": {
                    "type": "string",
                    "description": "Payment due date, YYYYMMDD — required for services."
                  }
                }
              },
              "eg": {
                "type": "object",
                "description": "Egypt (ETA).",
                "properties": {
                  "taxpayerActivityCode": {
                    "type": "string",
                    "description": "ETA taxpayer activity code override; defaults to the entity's eg_taxpayer_activity_code."
                  }
                }
              },
              "my": {
                "type": "object",
                "description": "Malaysia (MyInvois).",
                "properties": {
                  "sstRegistrationNumber": {
                    "type": "string",
                    "description": "SST registration number override; defaults to the entity's my_sst_registration_number. Omit for a supplier that is not SST-registered (NA is emitted)."
                  }
                }
              },
              "il": {
                "type": "object",
                "description": "Israel (ITA).",
                "properties": {
                  "branchId": {
                    "type": "string",
                    "description": "Branch ID, for multi-branch businesses."
                  },
                  "invoiceNote": {
                    "type": "string",
                    "description": "Invoice note. Falls back to the invoice-level note."
                  }
                }
              },
              "jo": {
                "type": "object",
                "description": "Jordan (JoFotara).",
                "properties": {
                  "invoiceType": {
                    "type": "string",
                    "enum": [
                      "income",
                      "general_sales",
                      "special_sales"
                    ],
                    "default": "general_sales",
                    "description": "Taxpayer's registered invoice type — determines the payment-method code family."
                  },
                  "paymentMethod": {
                    "type": "string",
                    "enum": [
                      "cash",
                      "receivable"
                    ],
                    "default": "cash"
                  },
                  "uuid": {
                    "type": "string",
                    "description": "cbc:UUID for this invoice (v4). Auto-generated if omitted.",
                    "format": "uuid"
                  },
                  "customerIdType": {
                    "type": "string",
                    "enum": [
                      "NIN",
                      "PN",
                      "TIN"
                    ],
                    "default": "NIN",
                    "description": "Customer identification scheme: national ID, passport or tax number."
                  },
                  "originalInvoiceUuid": {
                    "type": "string",
                    "description": "Required for a credit_note: the original invoice's JoFotara-assigned UUID."
                  },
                  "originalFullAmount": {
                    "type": "number",
                    "description": "Required for a credit_note: the original invoice's full gross (tax-inclusive) amount."
                  }
                }
              },
              "peppol": {
                "type": "object",
                "description": "Peppol network options.",
                "properties": {
                  "selfBilling": {
                    "type": "boolean",
                    "description": "Self-billing document (the customer issues on the seller's behalf); switches to the self-billing profile. See the prose above for per-country behaviour."
                  }
                }
              }
            }
          },
          "notifyCustomer": {
            "type": "boolean",
            "description": "Only meaningful for countries where no authority network delivers the invoice to the customer (DE always; ES only when this entity's Spanish invoices report under VeriFactu rather than SII — SII is a background report to AEAT decoupled from customer delivery, so this is silently ignored while SII is the active regime; IT only when the customer has no Italian SDI routing code; PL only when the customer has no Polish NIP — KSeF is pull-only and pull access requires the customer's own registered NIP). When the customer's email is set via customer.contact.email, Clearvo emails them a link to a hosted page to view/download the invoice. Omit to defer to the entity's own default (see PATCH /v1/entities/{id} notifyCustomerByDefault); true/false here overrides that default for this invoice only. Silently ignored (see customerNotification in the response) for countries Clearvo already delivers electronically (Peppol, IT B2B/B2G, PL when the customer has a NIP, RO, HU, GR, AR, FR — France delivers through an accredited third-party platform), or that are a pure background report to the tax authority with no customer-delivery role of their own (PT, and ES while its SII regime is active)."
          },
          "rejectInsteadOfAutoCorrect": {
            "type": "boolean",
            "default": false,
            "description": "Per-request opt-out from silent auto-correction. Defaults to false — an ADJUSTMENT rule match (see GET /v1/validations for the live catalogue of autoCorrectable checks) is silently applied and reported in the response's `adjustments[]`. Set to true and a would-be correction is never applied — it surfaces instead as a blocking entry in the rejection response's `errors[]`, so an integrator who wants to see and fix raw values themselves (rather than have Clearvo silently normalize them) can require that."
          },
          "ossDeclared": {
            "type": "boolean",
            "description": "Explicit declaration that this transaction is (true) or is not (false) reported through the seller's OSS (One Stop Shop) return — an input to Compliance Mandate resolution, not a tax treatment. Omit it and the platform derives OSS-ness itself: an active OSS registration (Union or non-Union scheme) whose window covers the transaction's tax point date, combined with a qualifying intra-EU B2C distance sale (a client tax code that resolves to a distance-sale scheme also counts as an explicit signal). Supply it and the explicit value always wins over derivation. A non-boolean value is rejected with `INVALID_OSS_DECLARED`."
          },
          "transactionDirection": {
            "type": "string",
            "enum": [
              "sale",
              "purchase"
            ],
            "default": "sale",
            "description": "Spain SII block 3 (received book / LFR). `'sale'` (default) — this entity's own outbound/issued document. `'purchase'` — a vendor's document the entity is recording on its own received side — Spain SII (received book) and France e-reporting (a purchase from a vendor not established in France is declared by you as the receiving party, at invoice level, in its own Received batch) only; every other live country's purchase-direction submission currently resolves to no reporting mandate at all (`mandate: NONE`). `supplier` is still the VENDOR/counterparty on a purchase (required — the entity's own Spanish registration for `customer`/Titular purposes is applied automatically, same derivation as the sale-side `supplier.taxId` rule)."
          },
          "shipFrom": {
            "type": "object",
            "properties": {
              "country": {
                "type": "string",
                "pattern": "^[A-Za-z]{2}$",
                "description": "ISO 3166-1 alpha-2."
              }
            },
            "description": "France e-reporting, own-goods stock transfers only (a client tax code with movement `own_goods_movement`). Where the goods leave: for an outbound transfer (sale) the French origin; for an inbound transfer (purchase) the origin member state. Used to find your own VAT registration in the other member state. Optional and ignored for every other transaction; omitting it never parks a row."
          },
          "shipTo": {
            "type": "object",
            "properties": {
              "country": {
                "type": "string",
                "pattern": "^[A-Za-z]{2}$",
                "description": "ISO 3166-1 alpha-2."
              }
            },
            "description": "France e-reporting, own-goods stock transfers only. Where the goods arrive. Needed only for an outbound transfer (shipFrom in France) to name the destination member state; ignored otherwise."
          },
          "accountingDate": {
            "type": "string",
            "format": "date",
            "description": "PURCHASE documents only (transactionDirection='purchase'), Spain SII. The accounting-entry date (FechaRegContable, art. 69 bis.1.b) RIVA) — anchors both the Compliance Mandate effective-date gate and the received-book (LFR) submission deadline. Optional even for a purchase (falls back to `taxPointDate ?? issueDate` when omitted) — but the vendor's own `issueDate` is legally the wrong date for this gate, so a caller that books on receipt should always supply it.",
            "example": "2026-09-02"
          },
          "deductibleTaxAmount": {
            "type": "number",
            "description": "CuotaDeducible — PURCHASE documents only, Spain SII. The deductible portion of input VAT on this purchase, always taken as given and never derived from the lines' own charged amounts (prorrata and non-deductible purchases exist; the platform must not invent this figure). An explicit `0` is a valid, distinct declaration (an exempt/non-deductible purchase) — NOT the same as omitting the field, which instead produces `NEEDS_INFO` naming this field."
          },
          "deductionPeriod": {
            "type": "object",
            "description": "Received book only, Spain SII. The VAT declaration period this deduction is actually taken in, when it differs from `accountingDate`'s own month (AEAT FAQ 4.9 Example 2: one invoice, two valid deadlines depending on the period the deduction is claimed in). Optional — omitted, the liquidation period defaults to `accountingDate`'s own month.",
            "properties": {
              "ejercicio": {
                "type": "string",
                "description": "4-digit year, e.g. '2026'."
              },
              "periodo": {
                "type": "string",
                "description": "2-digit month, e.g. '06'."
              }
            }
          }
        }
      },
      "InvoiceResponse": {
        "type": "object",
        "properties": {
          "companyCode": {
            "type": "string",
            "description": "The company code this record was stored under (the casing configured on the entity). Present only when a code resolved or the entity uses company codes; absent otherwise."
          },
          "companyCodeSource": {
            "type": "string",
            "enum": [
              "request",
              "entity_default",
              "none"
            ],
            "description": "Where companyCode came from: the request, or the entity's default ('none' when no code resolved, for example the entity default is outside its validity window, which adds a warning). Present only when a code resolved or the entity uses company codes. When the plan no longer includes company codes but a default is still configured, it keeps applying and a company_codes_entitlement_lapsed warning is added to warnings."
          },
          "ok": {
            "type": "boolean",
            "example": true,
            "description": "Not the outcome: branch on `outcome` / `clearanceStatus`. Derived from the status alone: `false` for `REJECTED`, `NEEDS_INFO`, `SETUP_NEEDED` (for example France `NEEDS_INFO`, Portugal `SETUP_NEEDED`) and `HELD_PLATFORM_FAULT`; `true` for every other status. A replay of the same document returns the same `ok`, the same `error`, `errors[]`, `fixUrl` and `nextStep` as the first answer. It is `false` on every dry run."
          },
          "id": {
            "type": "string",
            "description": "Clearvo's internal identifier for this submission — use this to poll GET /v1/status, fetch GET /v1/invoices/{id}, or reference via correctsInvoiceId on a later correction. Present on every answer that stores the document, including a rejection or hold, and on a replay of a document that is still being processed (the only way to find it).",
            "example": "EI-IT-a1b2c3d4"
          },
          "country": {
            "type": "string",
            "example": "IT"
          },
          "referenceId": {
            "type": "string",
            "nullable": true,
            "description": "Present only for ES (huella hash) and PT (ATCUD) — the country's own reference value, distinct from Clearvo's own `id`.",
            "example": "INV-2024-001"
          },
          "clearanceStatus": {
            "$ref": "#/components/schemas/ClearanceStatus"
          },
          "clearanceStatusLabel": {
            "type": "string",
            "description": "Human-readable label for clearanceStatus, ready to render directly.",
            "example": "Awaiting authority response"
          },
          "statusClass": {
            "allOf": [{ "$ref": "#/components/schemas/StatusClass" }],
            "description": "Who the document is waiting for. See StatusClass."
          },
          "nextStep": {
            "allOf": [{ "$ref": "#/components/schemas/NextStep" }],
            "nullable": true
          },
          "terminal": {
            "type": "boolean",
            "description": "True when no further status changes will occur.",
            "example": false
          },
          "submittedAt": {
            "type": "string",
            "format": "date-time"
          },
          "nextPollAfter": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Suggested time to poll again. Null for terminal statuses."
          },
          "sandbox": {
            "type": "boolean",
            "description": "True when using a csk_test_* key."
          },
          "mandate": {
            "type": "string",
            "nullable": true,
            "description": "The resolved Compliance Mandate this submission dispatched under (e.g. IT_EINVOICING, FR_EREPORTING, NONE), or null when the response was produced before dispatch resolution (e.g. a structural-validation failure). Part of the shared response envelope added to every successful outcome — purely additive: all pre-existing fields are unchanged.",
            "example": "IT_EINVOICING"
          },
          "outcome": {
            "$ref": "#/components/schemas/SendOutcome"
          },
          "disposition": {
            "type": "string",
            "description": "Human-readable label for `state`, ready to render directly.",
            "example": "Awaiting authority response"
          },
          "state": {
            "type": "string",
            "description": "The underlying fine-grained status `outcome` was derived from — same value as clearanceStatus. Prefer `outcome` for branching; `state` for display/debugging.",
            "example": "PENDING"
          },
          "rulesetVersion": {
            "type": "integer",
            "description": "Fingerprint of the exact validation-rules ruleset this request was evaluated against — sum of every applicable registry rule's own version. Matches GET /v1/validations's rulesetVersion for the same country when the registry hasn't changed in between."
          },
          "adjustments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Adjustment"
            },
            "description": "Auto-corrections silently applied before submission. Empty when nothing needed correcting, or when rejectInsteadOfAutoCorrect blocked every would-be correction instead of applying it."
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ValidationCheckResult"
            },
            "description": "Non-blocking WARNING-severity checks that matched. Present on every response, success or failure — never the reason for a rejection on its own. `PROCESSING_DELAYED` means a temporary problem interrupted processing after the document was stored: the response is 200 `RECEIVED` with `id`, Clearvo finishes the document automatically, and you do not need to resend it. Poll GET /v1/status or subscribe to the `invoice.status_changed` webhook."
          },
          "customerNotification": {
            "type": "object",
            "description": "Outcome of the customer invoice notification feature — only present for countries where no authority network delivers the invoice to the customer (see notifyCustomer on the request). A German invoice delivered over Peppol is not offered a copy by default (SKIPPED); notifyCustomer: true sends a copy visibly marked as a copy, not an original invoice.",
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "SENT",
                  "SKIPPED",
                  "FAILED"
                ]
              },
              "reason": {
                "type": "string",
                "enum": [
                  "NOT_APPLICABLE",
                  "NOT_OPTED_IN",
                  "MISSING_CUSTOMER_EMAIL",
                  "NOTIFICATIONS_NOT_CONFIGURED"
                ],
                "description": "Present only when status is SKIPPED."
              },
              "error": {
                "type": "string",
                "description": "Present only when status is FAILED."
              }
            }
          },
          "dryRun": {
            "type": "boolean",
            "description": "Echoed back only when the request set `dryRun: true`. A dry run answers HTTP 200 with the same body a real send would (`ok: false`, and the `clearanceStatus` and `errors[]` the document would be stored with) and persists nothing: no record, no `id`, nothing sent to an authority; `lines[]` below shows the preview resolution instead."
          },
          "lines": {
            "type": "array",
            "description": "Per-line tax-code resolution audit trail — one entry per submitted `lines[]`, in original order. Present on every real submission (and on a `dryRun: true` preview) except a credit note, where lines are forced to match the original invoice and this is omitted.",
            "items": {
              "type": "object",
              "properties": {
                "lineNumber": {
                  "type": "integer",
                  "description": "1-based line position."
                },
                "taxResolution": {
                  "$ref": "#/components/schemas/LineTaxResolution"
                }
              }
            }
          },
          "reportBy": {
            "type": "string",
            "format": "date",
            "nullable": true,
            "description": "Statutory report-by deadline for this transaction under its mandate — for Spain SII, the AEAT deadline (4 business days from issue, 8 when issued by a third party, Spanish national holidays excluded). The same value GET /v1/mandate-transactions surfaces as reportByDate. Null when no deadline was computed (non-SII mandates today, or the deadline lookup failed non-fatally)."
          },
          "siiClassification": {
            "type": "object",
            "description": "Spain SII only — the audit-trail summary of how this invoice was classified for AEAT at submit time. Never carries the AEAT CSV (AEAT has not answered yet at /v1/send time — see siiDetail on GET /v1/invoices/{id} once the submission completes). Absent for every non-SII submission.",
            "properties": {
              "book": {
                "type": "string",
                "enum": [
                  "issued"
                ],
                "description": "SII ledger book. Only the issued-invoices book (Libro de Facturas Expedidas) is dispatched via /v1/send today."
              },
              "tipoFactura": {
                "type": "string",
                "description": "AEAT TipoFactura (F1, F2, R1–R5, …).",
                "example": "F1"
              },
              "claveRegimen": {
                "type": "string",
                "nullable": true,
                "description": "ClaveRegimenEspecialOTrascendencia ('01'..'17') resolved for the first line.",
                "example": "01"
              },
              "tipoComunicacion": {
                "type": "string",
                "enum": [
                  "A0",
                  "A1"
                ],
                "description": "A0 = original registration, A1 = amendment of an already-registered invoice."
              },
              "ejercicio": {
                "type": "string",
                "description": "Fiscal year of the AEAT period the registro is filed in.",
                "example": "2026"
              },
              "periodo": {
                "type": "string",
                "description": "AEAT period (month, '01'..'12').",
                "example": "09"
              },
              "isLate": {
                "type": "boolean",
                "nullable": true,
                "description": "Preliminary, submit-time read: true when submittedAt is already past reportBy. Null when reportBy could not be computed. The definitive late flag is set at AEAT acceptance and read from GET /v1/mandate-transactions."
              }
            }
          },
          "reporting": {
            "type": "object",
            "description": "Portugal PT_EFATURA only — present when the entity has the `pt_efatura` reporting obligation enabled (PATCH /v1/tax/reporting-obligations) and this PT sale therefore dispatched under mandate PT_EFATURA rather than PT_EINVOICING. The document itself is issued identically either way (AT-signed, clearanceStatus ACCEPTED, terminal true, referenceId/atcud set — those fields describe the DOCUMENT and never change afterwards); this block tracks the SEPARATE invoice-data communication to AT that has just been queued. Follow it via ptReportingDetail on GET /v1/invoices/{id}, or the invoice.reported / invoice.report_rejected webhooks. Absent for every other country and for a PT entity without pt_efatura enabled.",
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "PENDING"
                ],
                "description": "Always PENDING at /v1/send time — the communication runs asynchronously."
              },
              "mandate": {
                "type": "string",
                "enum": [
                  "PT_EFATURA"
                ]
              },
              "reportBy": {
                "type": "string",
                "format": "date",
                "nullable": true,
                "description": "Legal deadline for communicating this invoice's data to AT: the end of the invoice's issue month plus the PT_EFATURA mandate's configured deadline days (5 today — the 5th of the following month, Decreto-Lei n.º 198/2012). Also persisted as reportByDate on GET /v1/mandate-transactions, so the invoice.report_due_soon / invoice.report_overdue ladder applies. Null only if the deadline could not be computed."
              }
            }
          },
          "field": {
            "type": "string",
            "nullable": true,
            "description": "Portugal only, alongside errorCode PT_SAFT_NEEDS_INFO (clearanceStatus NEEDS_INFO): the dotted path of the invoice field the tax-authority file format cannot accept as submitted (e.g. a zero-rated line with no resolvable VAT exemption reason, a tax rate that does not match the invoice's fiscal region). Correct it and resubmit under a new x-idempotency-key — nothing was issued and no sequence number was consumed."
          },
          "holdReason": {
            "type": "string",
            "nullable": true,
            "description": "Present when clearanceStatus is a HELD_* value or NEEDS_INFO — plain-language reason the transaction is held, mirroring the same field on GET /v1/mandate-transactions."
          },
          "actionOwner": {
            "type": "string",
            "nullable": true,
            "enum": [
              "platform",
              "customer"
            ],
            "description": "Who must act to clear a held/needs-info transaction: `platform` for a configuration or coverage gap on Clearvo's side (HELD_UNMAPPED_DECISION, HELD_UNKNOWN_MANDATE, HELD_PLATFORM_FAULT — never the merchant's fault), `customer` for a data gap only the merchant can fill (NEEDS_INFO)."
          },
          "errorCode": {
            "type": "string",
            "nullable": true,
            "description": "Machine-readable code for a non-success outcome on a 200 — e.g. for HELD_UNMAPPED_TAX_CODE the tax-code resolver's own code (UNKNOWN_CLIENT_TAX_CODE, UNSEEDED_TAX_TREATMENT, UNKNOWN_SOURCE_SYSTEM_CODE — see `reason` for the structured diagnostic), HELD_UNMAPPED_DECISION (any country: no compliance-mandate rule matched this tax decision; actionOwner `platform`), MANDATE_INCOMPLETE_DATA (NEEDS_INFO, aggregate e-reporting), MISSING_SUPPLIER_REGISTRATION (NEEDS_INFO: Spain SII/VeriFactu resolved but the entity has no current Spanish registration as of the document date) or, on a `transactionDirection: 'purchase'` Spain SII received-book document, its counterpart MISSING_CUSTOMER_REGISTRATION (same NEEDS_INFO shape — the entity is the `customer` on a purchase, so it's the entity's own missing/lapsed registration either way), Portugal's PT_SERIES_NOT_CONFIGURED (SETUP_NEEDED: no AT series registered for this document type — no NC/ND fallback onto the FT series), PT_SERIES_CODE_MISMATCH (SETUP_NEEDED: the daily AT check found the stored series' validation code no longer matches AT's record — re-check and re-save via POST /v1/pt/credentials) and PT_SAFT_NEEDS_INFO (NEEDS_INFO: the document cannot become a valid SAF-T (PT) fragment as submitted — see `field`; nothing issued, no sequence number consumed), or, for a Spain SII batch-mode record whose local structural pre-check failed, the AEAT-style check code (NEEDS_INFO rather than REJECTED — a batch record never reaches AEAT at submit time, so it is held for a corrected resubmission under a new x-idempotency-key). Absent on a clean CLEARED outcome.",
            "example": "UNKNOWN_CLIENT_TAX_CODE PEPPOL_SCHEMATRON_VALIDATION_FAILED and XRECHNUNG_SCHEMATRON_VALIDATION_FAILED (Germany): the generated invoice failed schema / EN 16931 validation and is held as NEEDS_INFO, with `validationReport`."
          },
          "message": {
            "type": "string",
            "nullable": true,
            "description": "Plain-language explanation accompanying errorCode / a held (including HELD_UNMAPPED_TAX_CODE) or accumulated outcome, ready to render directly."
          },
          "reason": {
            "allOf": [
              {
                "$ref": "#/components/schemas/UnmappedTaxCodeReason"
              }
            ],
            "description": "Present only when clearanceStatus is HELD_UNMAPPED_TAX_CODE — the machine-readable diagnostic for which code wasn't configured and where to fix it."
          },
          "fixUrl": {
            "type": "string",
            "nullable": true,
            "description": "Absolute deep link into the dashboard where the merchant can resolve a NEEDS_INFO/held outcome (e.g. the Registrations page for MISSING_SUPPLIER_REGISTRATION / MISSING_CUSTOMER_REGISTRATION). Built on the calling tenant's own host. Absent when there is nothing to fix."
          },
          "mandateWarnings": {
            "type": "array",
            "description": "Non-blocking dispatch warnings. Present only when one applies — today SUBMISSION_MODE_NOT_ALLOWED_FALLBACK: the entity's stored submission mode for the regime is not one the regime allows, so the platform dispatched as `immediate` instead.",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string",
                  "example": "SUBMISSION_MODE_NOT_ALLOWED_FALLBACK"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          },
          "documentFormat": {
            "type": "string",
            "nullable": true,
            "enum": [
              "UBL_XRECHNUNG",
              "CII_ZUGFERD",
              "UBL_PEPPOL_BIS"
            ],
            "description": "DE only. Which format was actually generated — resolved most-specific-wins (see countrySpecific.de.invoiceFormat on the request, and formatResolvedFrom below). UBL_PEPPOL_BIS means the invoice goes to the customer over the Peppol network: the response is clearanceStatus PENDING (never ACCEPTED at generation) and moves to DELIVERED once the receiving access point confirms; no invoice.accepted webhook fires at generation. Null/absent for every other country."
          },
          "profile": {
            "type": "string",
            "nullable": true,
            "description": "DE only. 'XRECHNUNG', or the ZUGFeRD CII profile/conformance level (e.g. 'EN 16931')."
          },
          "specVersion": {
            "type": "string",
            "nullable": true,
            "description": "DE only. 'XRECHNUNG_3.0', or the ZUGFeRD spec version."
          },
          "formatResolvedFrom": {
            "type": "string",
            "nullable": true,
            "enum": [
              "request",
              "customer",
              "entity",
              "platform"
            ],
            "description": "DE only. Which level of the most-specific-wins format resolution decided documentFormat for this send: this request's own countrySpecific.de.invoiceFormat, a stored per-customer default, a per-entity default, or the platform default (ZUGFERD)."
          },
          "delivery": {
            "type": "object",
            "description": "Present for a German PEPPOL send (and its dryRun): how the invoice reaches the customer. A German customer's Peppol address is never derived from a VAT number — it must be supplied.",
            "properties": {
              "channel": {
                "type": "string",
                "enum": [
                  "NETWORK"
                ],
                "description": "Always NETWORK: delivered over the Peppol network."
              },
              "receiver": {
                "type": "string",
                "nullable": true,
                "description": "The customer's Peppol address in `scheme:value` form, e.g. 9930:DE123456789."
              },
              "evidence": {
                "type": "object",
                "additionalProperties": true,
                "description": "dryRun only — what the reachability check looked at."
              }
            }
          },
          "validationReport": {
            "type": "object",
            "additionalProperties": true,
            "description": "Present when a PEPPOL or XRECHNUNG invoice failed the pre-send schema / EN 16931 (Schematron) validation: the invoice is stored as NEEDS_INFO with errorCode PEPPOL_SCHEMATRON_VALIDATION_FAILED or XRECHNUNG_SCHEMATRON_VALIDATION_FAILED (on a 200) and this carries the findings. Correct the data and resubmit with correctsInvoiceId to reuse the invoice number."
          },
          "documents": {
            "type": "object",
            "nullable": true,
            "description": "DE only. Fetch URLs for this document's stored representations, each a relative path on this same API passed straight through to GET /v1/documents/{id}?format=....",
            "properties": {
              "pdfUrl": {
                "type": "string",
                "nullable": true,
                "description": "ZUGFeRD only — the hybrid PDF/A-3 (format=pdf). Absent for XRechnung."
              },
              "xmlUrl": {
                "type": "string",
                "description": "The structured XML — CII for ZUGFeRD, UBL for XRechnung (format=xml)."
              },
              "originalUrl": {
                "type": "string",
                "nullable": true,
                "description": "ZUGFeRD only — the exact archived original bytes (format=original), byte-identical to what was generated. Absent for XRechnung, whose small XML file IS documents.xmlUrl's own content — no separate archive round-trip."
              }
            }
          },
          "reviewRequired": {
            "type": "boolean",
            "description": "True when the outcome needs a human decision before it is clean: outcome HELD or NEEDS_INFO, or a Spain SII acceptance with admissible errors (errorCode SII_ADMISSIBLE_ERRORS). False for ordinary cleared/accumulated responses and for a terminal REJECTED (which has its own Fix & Resubmit flow). Present on responses carrying the outcome envelope."
          },
          "fileName": {
            "type": "string",
            "description": "Name of the generated document file (e.g. the XML), where one is produced synchronously (ES VeriFactu, PT, SA, IL, ...). Also returned on an idempotent replay."
          },
          "sessionId": {
            "type": "string",
            "description": "Authority session identifier, returned on an idempotent replay when one exists."
          },
          "transactionId": {
            "type": "string",
            "description": "Parent compliance-mandate transaction ID (see GET /mandate-transactions/{id}), present once one has been resolved."
          },
          "huella": {
            "type": "string",
            "description": "Spain VeriFactu only: the full 64-character SHA-256 hash of the generated record (referenceId carries its first 16 characters)."
          },
          "qrData": {
            "type": "string",
            "description": "Verification QR payload where the country generates one synchronously: Spain VeriFactu, Portugal (AT), Saudi Arabia (ZATCA)."
          },
          "invoiceHash": {
            "type": "string",
            "description": "Saudi Arabia (ZATCA) only: the invoice hash of the generated document."
          },
          "missingFields": {
            "type": "array",
            "description": "Present with clearanceStatus NEEDS_INFO and errorCode MANDATE_INCOMPLETE_DATA: every field the resolved mandate requires that the submission did not supply.",
            "items": {
              "type": "object",
              "required": [
                "field",
                "reason"
              ],
              "properties": {
                "field": {
                  "type": "string",
                  "description": "Machine-readable field name."
                },
                "reason": {
                  "type": "string",
                  "description": "Why this mandate requires the field, in plain English."
                }
              }
            }
          },
          "idempotent": {
            "type": "boolean",
            "description": "True when this response replays the stored result of an earlier request with the same idempotency key / document identity rather than processing a new submission."
          },
          "reevaluated": {
            "type": "boolean",
            "description": "Present (true) with idempotent when a previously held transaction was re-evaluated under the current rules on retry (e.g. a HELD_UNMAPPED_DECISION hold that has since been resolved)."
          },
          "allocationRequired": {
            "type": "boolean",
            "description": "Israel only: whether the invoice needed an allocation number from the Israel Tax Authority (true while it is PENDING that clearance; false when none was required)."
          }
        }
      },
      "PeppolCustomerUnreachableResponse": {
        "type": "object",
        "description": "The HTTP 200 body for a German invoice that resolved to the PEPPOL format whose customer cannot be reached over Peppol: no explicit Peppol address (a German customer's address is never derived from a VAT number — reason NO_CUSTOMER_PEPPOL_ID) or an address the Peppol network does not know (reason NOT_REGISTERED_ON_PEPPOL). There is no silent fallback: the invoice is stored as NEEDS_INFO (fix the address, or re-send in another format with `correctsInvoiceId`), and `error.alternatives[]` lists the formats to re-send as. Also returned by `dryRun: true`, in which case nothing is stored.",
        "required": [
          "ok",
          "error"
        ],
        "properties": {
          "ok": {
            "type": "boolean",
            "enum": [
              false
            ]
          },
          "id": {
            "type": "string",
            "description": "The stored NEEDS_INFO record. Absent on a dryRun."
          },
          "clearanceStatus": {
            "type": "string",
            "enum": [
              "NEEDS_INFO"
            ]
          },
          "error": {
            "type": "object",
            "required": [
              "code",
              "message",
              "reason",
              "alternatives",
              "evidence"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "PEPPOL_CUSTOMER_UNREACHABLE"
                ]
              },
              "message": {
                "type": "string"
              },
              "reason": {
                "type": "string",
                "enum": [
                  "NO_CUSTOMER_PEPPOL_ID",
                  "NOT_REGISTERED_ON_PEPPOL"
                ]
              },
              "isMerchantResolvable": {
                "type": "boolean"
              },
              "fixUrl": {
                "type": "string"
              },
              "retryable": {
                "type": "boolean"
              },
              "alternatives": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "invoiceFormat": {
                      "type": "string",
                      "enum": [
                        "ZUGFERD",
                        "XRECHNUNG"
                      ]
                    },
                    "label": {
                      "type": "string"
                    },
                    "description": {
                      "type": "string"
                    },
                    "resend": {
                      "type": "object",
                      "additionalProperties": true,
                      "description": "Request fragment to re-send with (`countrySpecific.de.invoiceFormat`, and `correctsInvoiceId` to reuse the invoice number)."
                    }
                  }
                }
              },
              "evidence": {
                "type": "object",
                "additionalProperties": true,
                "description": "What was checked and found; also stored on the record's event trail."
              }
            }
          }
        }
      },
      "LineTaxResolution": {
        "type": "object",
        "description": "The full Tax Decision behind one line's resolution — how the caller's clientTaxCode/taxTreatment/taxRate input became an EN16931/BIS category. Persisted once at send time; never rewritten by a later edit of the client tax code that produced it.",
        "properties": {
          "resolvedBy": {
            "type": "string",
            "enum": [
              "client_tax_code",
              "source_system_code",
              "facts"
            ],
            "description": "Which tier resolved this line: an entity-configured client_tax_codes row, a connector's own system_enum row (e.g. Xero TaxType), or the facts tier (the line's stated facts — taxRate and any taxTreatment — mapped as given, never inferred from the customer/seller country pair)."
          },
          "input": {
            "type": "object",
            "description": "The exact input this resolution consumed — exactly one of clientTaxCode, sourceSystemCode, or facts is present, matching resolvedBy.",
            "properties": {
              "clientTaxCode": {
                "type": "string"
              },
              "sourceSystemCode": {
                "type": "object",
                "properties": {
                  "system": {
                    "type": "string"
                  },
                  "code": {
                    "type": "string"
                  }
                }
              },
              "facts": {
                "type": "object",
                "properties": {
                  "customerCountry": {
                    "type": "string"
                  },
                  "customerTaxId": {
                    "type": "string"
                  },
                  "customerType": {
                    "type": "string",
                    "enum": [
                      "b2b",
                      "b2c"
                    ]
                  },
                  "supplyType": {
                    "type": "string",
                    "enum": [
                      "goods",
                      "digital_service",
                      "general_service"
                    ]
                  },
                  "taxTreatment": {
                    "type": "string"
                  }
                }
              }
            }
          },
          "mappingRowId": {
            "type": "string",
            "nullable": true,
            "description": "The tax_code_mappings/client_tax_codes row id this decision was resolved from. Null for a facts-tier line with no taxTreatment (mapped directly from the stated facts, backed by no single mapping row)."
          },
          "mappingVersion": {
            "type": "integer",
            "nullable": true,
            "description": "tax_code_mappings.version — null for a client_tax_codes-sourced row (that table has no version column) or a facts-tier line with no taxTreatment."
          },
          "category": {
            "type": "string",
            "description": "The resolved EN16931/BIS output category (S/Z/E/AE/K/G/O/L/M).",
            "example": "S"
          },
          "exemptionReason": {
            "type": "string",
            "nullable": true
          },
          "band": {
            "type": "string",
            "nullable": true,
            "description": "Resolved rate band (standard/middle/reduced/super_reduced/special/zero), when applicable."
          },
          "rate": {
            "type": "number",
            "description": "The taxRate this line was resolved against, as a percentage."
          }
        }
      },
      "UnmappedTaxCodeReason": {
        "type": "object",
        "description": "The machine-readable diagnostic returned when a line/shipping/allowance-charge's clientTaxCode or taxTreatment isn't configured for this entity yet — sets clearanceStatus to HELD_UNMAPPED_TAX_CODE rather than rejecting the request.",
        "properties": {
          "source_system": {
            "type": "string",
            "nullable": true,
            "description": "Which connector/ERP this code was read from (e.g. 'sap'), when the caller supplied one — purely diagnostic, never consulted by the resolver itself."
          },
          "code": {
            "type": "string",
            "nullable": true,
            "description": "The unconfigured code or taxTreatment value."
          },
          "country": {
            "type": "string",
            "nullable": true
          },
          "direction": {
            "type": "string",
            "nullable": true,
            "enum": [
              "sale",
              "purchase"
            ]
          },
          "deepLink": {
            "type": "string",
            "description": "Dashboard deep link, pre-filled, for configuring this code.",
            "example": "/tax-codes?new=1&code=A1"
          },
          "codeSource": {
            "type": "string",
            "enum": [
              "caller",
              "default"
            ],
            "description": "Who chose the held code. `caller` — you supplied it (`clientTaxCode` on the line/header, or a `tax_code` cell on a bulk CSV row). `default` — the platform injected it: a bulk CSV row with a blank `tax_code` receives the platform default `S`, which must itself be registered for this entity via POST /v1/tax/client-codes, otherwise every such row is held here. Optional: absent on records stored before this field existed."
          }
        },
        "required": [
          "source_system",
          "code",
          "country",
          "direction",
          "deepLink"
        ]
      },
      "StatusClass": {
        "type": "string",
        "enum": ["IN_FLIGHT", "WAIT_MERCHANT", "WAIT_PLATFORM", "DONE_OK", "DONE_REJECTED", "DONE_NA"],
        "description": "Who a document is waiting for. IN_FLIGHT: we are working on it (RECEIVED, PENDING, ACCUMULATED). WAIT_MERCHANT: only you can move it, see `nextStep` (UNROUTABLE, SETUP_NEEDED, HELD_UNMAPPED_TAX_CODE, NEEDS_INFO, OBLIGATION_DISABLED, RECEIVED_NEEDS_REVIEW); it never expires. WAIT_PLATFORM: we resolve it, nothing for you to do (UNDELIVERED, HELD_AWAITING_AUTHORIZATION, HELD_UNKNOWN_MANDATE, HELD_PLATFORM_FAULT, HELD_UNMAPPED_DECISION). DONE_OK: finished successfully (DELIVERED, ACCEPTED, CANCELLED). DONE_REJECTED: finished, refused by the authority or a rule (REJECTED). DONE_NA: finished, nothing was or will be submitted (DUPLICATE, NOT_SUPPORTED, NO_MANDATE, EXCLUDED)."
      },
      "NextStep": {
        "type": "object",
        "nullable": true,
        "required": ["actor", "message"],
        "description": "What has to happen next, who does it and where. Null when nothing is outstanding (a finished status with nothing for anyone to do).",
        "properties": {
          "actor": { "type": "string", "enum": ["merchant", "platform", "authority", "system"], "description": "Who has to act. `system` means we are already working on it and nothing is needed from you." },
          "message": { "type": "string", "description": "Plain-language instruction." },
          "fixUrl": { "type": "string", "description": "Where you can fix it yourself, when you can." }
        }
      },
      "ClearanceStatus": {
        "type": "string",
        "enum": [
          "RECEIVED",
          "PENDING",
          "ACCEPTED",
          "REJECTED",
          "DELIVERED",
          "UNROUTABLE",
          "UNDELIVERED",
          "DUPLICATE",
          "ACCUMULATED",
          "NEEDS_INFO",
          "EXCLUDED",
          "NO_MANDATE",
          "SETUP_NEEDED",
          "HELD_AWAITING_AUTHORIZATION",
          "HELD_UNKNOWN_MANDATE",
          "OBLIGATION_DISABLED",
          "HELD_UNMAPPED_DECISION",
          "HELD_PLATFORM_FAULT",
          "HELD_UNMAPPED_TAX_CODE",
          "NOT_SUPPORTED",
          "CANCELLED",
          "RECEIVED_NEEDS_REVIEW"
        ],
        "x-enumDescriptions": {
          "RECEIVED": "The document is stored and processing has not finished. Normally seen only for a moment; if processing is interrupted Clearvo restarts it automatically. Non-terminal, class IN_FLIGHT. Nothing to do. A replay of a document that is still in this state returns its `id`, the only way to find it.",
          "PENDING": "Submitted to authority, awaiting response. Poll again after nextPollAfter.",
          "ACCEPTED": "Authority accepted and/or legally registered the invoice. Does not by itself mean the customer received a copy — see DELIVERED. For Italy specifically: ACCEPTED with a B2C/non-Italian-customer placeholder Codice Destinatario means SDI validated the invoice but had no real recipient channel to deliver it to. Terminal.",
          "REJECTED": "Authority rejected the invoice. See rejectionReason. Terminal.",
          "DELIVERED": "The invoice was genuinely delivered to the customer over a real network — via Peppol AS4, or via Italy SDI when the customer's Codice Destinatario is a real routing channel (not the B2C/non-Italian-customer placeholder, which stays ACCEPTED instead). Terminal.",
          "UNROUTABLE": "Customer not found in Peppol SML. Non-terminal — retry via POST /v1/invoices/{id}/deliver.",
          "UNDELIVERED": "Delivery attempt failed — a Peppol AS4 attempt, or Italy SDI reporting the customer's channel unreachable (MC). Non-terminal; Peppol retries via POST /v1/invoices/{id}/deliver, Italy retries automatically within its statutory window.",
          "DUPLICATE": "Idempotency key already used within 24 h. Cached response returned. Terminal.",
          "HELD_UNMAPPED_TAX_CODE": "A line, `shipping`, or an allowance/charge supplied a `clientTaxCode` or `taxTreatment` this entity hasn't configured (via POST /v1/tax/client-codes) yet. Never a malformed request — the invoice is accepted and held with a machine-readable reason (see `errorCode`/`message`/`reason` on the response, and the UnmappedTaxCodeReason schema). Non-terminal: configure the missing code and resubmit under a NEW x-idempotency-key to unblock it.",
          "ACCUMULATED": "The transaction resolved to an aggregate e-reporting mandate (e.g. FR_EREPORTING) and was accepted into a reporting period for later aggregate submission — see the `period` and `deadline` fields on the response. No per-document clearance will ever occur. Non-terminal until the period is filed.",
          "NEEDS_INFO": "Resolved to an aggregate e-reporting mandate but is missing a field that mandate requires (see missingFields). Parked, not accumulated — supply the missing data via a corrected resubmission under a new x-idempotency-key, or the PATCH /fr/transactions/needs-info/{id} resupply endpoint. Non-terminal; carries the affected period's filing deadline.",
          "EXCLUDED": "A matched compliance-mandate rule (or a validation-rules exclusion) determined no obligation applies to this transaction — e.g. an OSS-declared distance sale excluded from FR e-reporting. Recorded and retained, nothing further happens. Terminal.",
          "NO_MANDATE": "No compliance obligation applies (no rule matched and no country default exists). Recorded and retained only. Terminal.",
          "SETUP_NEEDED": "A real dispatch path exists but merchant setup is incomplete (e.g. missing authority credentials, or an unconfirmed FR VAT regime / e-reporting go-live). Non-terminal — completing setup unblocks the stored submission automatically.",
          "HELD_AWAITING_AUTHORIZATION": "Held because Clearvo cannot submit to this authority yet: a country that is not yet enabled for your account, or another platform-side gate. `errorCode` is MANDATE_SUPPORT_NOT_OPERATIONAL for the support gates. Non-terminal, class WAIT_PLATFORM: nothing is needed from you, it is released automatically.",
          "HELD_UNKNOWN_MANDATE": "The resolved mandate has no registered dispatch strategy — held with an internal alert, never silently dropped. Non-terminal.",
          "OBLIGATION_DISABLED": "The entity's own reporting-obligation toggle for this mandate is off (see PATCH /tax/reporting-obligations). Held; re-enabling the obligation re-processes held rows automatically. Non-terminal.",
          "HELD_UNMAPPED_DECISION": "Mandate resolution found no compliance-mandate rule for this tax decision under the entity's enabled regimes — a configuration gap on the platform side (a decision nobody wrote a rule for), never the merchant's fault and never silently treated as no-obligation. Held, alerted, actionOwner `platform`; clears automatically once a rule is added and the transaction is re-resolved. Fires the held_unmapped_decision webhook. Non-terminal.",
          "HELD_PLATFORM_FAULT": "The authority was reached but answered with something that is nobody's invoice-data fault — a whole-envelope authority-side platform/schema fault, a submitting-certificate authorisation gap (e.g. AEAT SII 4114), or a duplicate response the platform cannot correlate to an already-accepted record of its own. Never merchant-resolvable; held with an internal alert until an engineer investigates and reprocesses. Non-terminal.",
          "NOT_SUPPORTED": "The document is dated before the date Clearvo supports this mandate (`errorCode: MANDATE_NOT_SUPPORTED`), or a Spain VeriFactu invoice is outside the mandate window. It is stored but never sent to an authority, not even later when support starts. Terminal, class DONE_NA. A document dated on or after the support start date for a country that is not open yet is HELD_AWAITING_AUTHORIZATION instead.",
          "CANCELLED": "Brazil received documents only: the NF-e/NFS-e was cancelled or substituted by its issuer after it was received. Terminal.",
          "RECEIVED_NEEDS_REVIEW": "Received (inbound) documents only: the document landed but its validation outcome was anything other than a legally sufficient e-invoice, so it is flagged for manual review. See `validation` on the invoice."
        }
      },
      "SendOutcome": {
        "type": "string",
        "enum": [
          "CLEARED",
          "REJECTED",
          "ACCUMULATED",
          "NEEDS_INFO",
          "NO_OBLIGATION",
          "HELD",
          "PROCESSING"
        ],
        "description": "The coarse, mandate-class-agnostic outcome of a submission — the one stable field to branch on regardless of which country/mandate pipeline handled the document. Derived from the fine-grained `state`.",
        "x-enumDescriptions": {
          "CLEARED": "A per-document mandate processed the document and did not reject it (PENDING, ACCEPTED, DELIVERED, DUPLICATE or CANCELLED). RECEIVED (PROCESSING), UNROUTABLE (NEEDS_INFO) and UNDELIVERED (HELD) are not CLEARED.",
          "REJECTED": "The per-document pipeline ran and the outcome is a rejection — never folded into CLEARED, including on idempotent-retry responses.",
          "ACCUMULATED": "An aggregate e-reporting mandate accepted the transaction into a reporting period for later aggregate submission.",
          "NEEDS_INFO": "Resolved to an aggregate e-reporting mandate but missing a required field — parked, not yet accumulated.",
          "NO_OBLIGATION": "No compliance obligation applies (rule-matched exclusion, or no mandate and no country default). Recorded-only.",
          "HELD": "A real dispatch path exists but is blocked pending something outside the caller's control — platform gate, incomplete merchant setup, unmapped mandate, unmapped tax decision (HELD_UNMAPPED_DECISION), authority-side platform fault (HELD_PLATFORM_FAULT), or a disabled obligation toggle. Check holdReason/actionOwner for the why and who. Also a failed delivery (UNDELIVERED) and the fail-safe for an unrecognized state.",
          "PROCESSING": "The document is stored (RECEIVED) but not processed yet, so there is no answer to act on. Poll GET /v1/status or wait for the invoice.status_changed webhook."
        }
      },
      "InvoiceAccumulatedResponse": {
        "type": "object",
        "description": "Returned with HTTP 200 when the submission is ACCUMULATED rather than dispatched per document: (a) an aggregate e-reporting mandate (e.g. FR_EREPORTING) folds the transaction into a reporting period — `period`/`deadline` describe it; or (b) a Spain SII (ES_SII) entity whose es_sii submissionMode is `batch_auto` or `batch_review` — the registro is generated and validated exactly as in `immediate` mode but joins the entity's open SII batch instead of being sent to AEAT right away; `batchId`/`reportBy`/`submissionMode`/`siiClassification` describe it (no `period`/`deadline`). Track the batch via GET /v1/reporting-batches/{id}; a `batch_review` batch is frozen ready_for_review at its deadline trigger and nothing is sent to AEAT until you POST /v1/reporting-batches/{id}/confirm (webhook reporting_batch.ready_for_review). Carries the same additive envelope fields (mandate/outcome/disposition/state/reviewRequired) as InvoiceResponse.",
        "properties": {
          "ok": {
            "type": "boolean",
            "example": true
          },
          "id": {
            "type": "string",
            "description": "Clearvo's internal identifier for this transaction — usable with GET /v1/mandate-transactions/{id}."
          },
          "country": {
            "type": "string",
            "example": "FR"
          },
          "sandbox": {
            "type": "boolean"
          },
          "clearanceStatus": {
            "type": "string",
            "example": "ACCUMULATED"
          },
          "terminal": {
            "type": "boolean",
            "example": false
          },
          "mandate": {
            "type": "string",
            "example": "FR_EREPORTING"
          },
          "outcome": {
            "$ref": "#/components/schemas/SendOutcome"
          },
          "disposition": {
            "type": "string"
          },
          "state": {
            "type": "string",
            "example": "ACCUMULATED"
          },
          "period": {
            "type": "object",
            "description": "The reporting period this transaction was assigned to, keyed on its tax point date.",
            "properties": {
              "key": {
                "type": "string",
                "description": "Period key, e.g. '2026-09-D1' for an FR décade.",
                "example": "2026-09-D1"
              },
              "start": {
                "type": "string",
                "format": "date",
                "nullable": true
              },
              "end": {
                "type": "string",
                "format": "date",
                "nullable": true
              }
            }
          },
          "deadline": {
            "type": "string",
            "format": "date",
            "nullable": true,
            "description": "Expected filing date for that period — computed from the same per-mandate cadence/deadline configuration the submission scheduler itself uses, so it can never drift from the real filing calendar. Null when the mandate's deadline configuration is unresolved."
          },
          "message": {
            "type": "string",
            "description": "Plain-language outcome, ready to render directly.",
            "example": "Included in your France e-reporting for period 2026-09-D1 — filing due 2026-09-13."
          },
          "submittedAt": {
            "type": "string",
            "format": "date-time"
          },
          "clearanceStatusLabel": {
            "type": "string",
            "description": "Human-readable label for clearanceStatus (ES_SII batch mode)."
          },
          "fileName": {
            "type": "string",
            "description": "ES_SII batch mode only — the stored registro XML file name."
          },
          "batchId": {
            "type": "string",
            "description": "ES_SII batch mode only — the human-readable key of the SII batch this record joined (the `batchId` on GET /v1/reporting-batches, whose `id` is the UUID used in the batch URLs). Absent for FR e-reporting (see `period` instead).",
            "example": "ES-SII-2026-09-ISSUED-1"
          },
          "reportBy": {
            "type": "string",
            "format": "date",
            "description": "ES_SII batch mode only — this record's own statutory AEAT report-by date (4 Spanish business days from issue, 8 when issued by a third party). The batch's own deadline is the earliest reportBy across its records (its `reportBy`/`periodEnd` on GET /v1/reporting-batches)."
          },
          "submissionMode": {
            "type": "string",
            "enum": [
              "batch_auto",
              "batch_review"
            ],
            "description": "ES_SII batch mode only — the es_sii submissionMode that routed this record into a batch. `batch_auto`: the batch is submitted to AEAT automatically when its deadline trigger fires. `batch_review`: the batch is frozen ready_for_review and waits for POST /v1/reporting-batches/{id}/confirm."
          },
          "reviewRequired": {
            "type": "boolean",
            "description": "True when a human must act before anything is filed — for a `batch_review` ES_SII record, the batch confirm."
          },
          "siiClassification": {
            "type": "object",
            "description": "ES_SII batch mode only — same shape as InvoiceResponse.siiClassification (book, tipoFactura, claveRegimen, tipoComunicacion, ejercicio, periodo, isLate). `isLate` here is the value persisted on the batch record: this record's own reportBy is already behind today at the time it joined the batch (a late arrival joins the CURRENT open batch, never a closed one)."
          }
        }
      },
      "MandateResolutionJourneyEntry": {
        "type": "object",
        "description": "One entry in a transaction's resolution journey — the append-only history of every Compliance Mandate resolution this transaction has carried, oldest first. A journey longer than one entry means a later event (e.g. a backdated OSS registration) superseded the original resolution.",
        "properties": {
          "seq": {
            "type": "integer",
            "description": "0 for the original resolution; increments per supersede."
          },
          "mandate": {
            "type": "string"
          },
          "triggerReason": {
            "type": "string",
            "description": "Why this resolution was recorded — e.g. INITIAL, REGISTRATION_BACKDATED."
          },
          "applied": {
            "type": "boolean",
            "description": "False when the new resolution could not be applied to the row itself (e.g. its period was already filed) and was instead honoured via a linked rectification — see rectificationTransactionId."
          },
          "resolvedAt": {
            "type": "string",
            "format": "date-time"
          },
          "matchedRuleId": {
            "type": "string",
            "nullable": true
          },
          "rectificationTransactionId": {
            "type": "string",
            "nullable": true
          },
          "factDerivation": {
            "type": "object",
            "nullable": true,
            "additionalProperties": true,
            "description": "How derived facts were obtained for this resolution (e.g. how ossDeclared was derived: explicit input, client tax code, or registration lookup)."
          }
        }
      },
      "MandateTransaction": {
        "type": "object",
        "description": "One row of the canonical, cross-jurisdiction transaction ledger — every transaction the platform has classified, whatever the outcome (per-document e-invoicing, aggregate e-reporting, or no obligation), with immutable resolution provenance.",
        "properties": {
          "id": {
            "type": "string"
          },
          "entityId": {
            "type": "string"
          },
          "country": {
            "type": "string"
          },
          "sourceChannel": {
            "type": "string",
            "description": "Which ingestion channel produced this row (e.g. API_SEND, BULK_CSV)."
          },
          "idempotencyKey": {
            "type": "string"
          },
          "state": {
            "type": "string",
            "description": "Raw ledger state: PENDING, RESOLVED, NEEDS_INFO, HELD_UNKNOWN_MANDATE, OBLIGATION_DISABLED or HELD_UNMAPPED_DECISION. The list endpoint's `state` filter also accepts the alias HELD, covering all three HELD_*/OBLIGATION_DISABLED values."
          },
          "holdReason": {
            "type": "string",
            "nullable": true,
            "description": "Plain-language reason this row is held or needs info (e.g. HELD_UNMAPPED_DECISION's \"no compliance-mandate rule matched\" message). Null for a row that was never held."
          },
          "actionOwner": {
            "type": "string",
            "nullable": true,
            "enum": [
              "platform",
              "customer"
            ],
            "description": "Who must act to clear a held/needs-info row: `platform` (an engineering/configuration gap — HELD_UNMAPPED_DECISION, HELD_UNKNOWN_MANDATE) or `customer` (a data gap only the merchant can fill — NEEDS_INFO). Null for a row that was never held."
          },
          "reportByDate": {
            "type": "string",
            "format": "date",
            "nullable": true,
            "description": "Statutory reporting deadline this transaction is running against (Spain SII: the AEAT report-by date), snapshotted at classification/hold time. Null when none has been computed."
          },
          "issueDate": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "taxPointDate": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "paymentDate": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "currency": {
            "type": "string",
            "nullable": true
          },
          "totalNet": {
            "type": "number",
            "nullable": true
          },
          "totalTax": {
            "type": "number",
            "nullable": true,
            "description": "Total tax on the document (tax-type-neutral; formerly `totalVat`)."
          },
          "totalGross": {
            "type": "number",
            "nullable": true
          },
          "payableAmount": {
            "type": "number",
            "nullable": true
          },
          "supplierTaxId": {
            "type": "string",
            "nullable": true
          },
          "customerTaxId": {
            "type": "string",
            "nullable": true
          },
          "isLate": {
            "type": "boolean",
            "description": "True when the transaction arrived after its own period had already closed — it keeps its original-period attribution and flows as late/corrective data."
          },
          "isRectification": {
            "type": "boolean"
          },
          "rectifiesTransactionId": {
            "type": "string",
            "nullable": true
          },
          "dispatchedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "mandate": {
            "type": "string",
            "nullable": true
          },
          "mandateClass": {
            "type": "string",
            "nullable": true,
            "enum": [
              "PER_DOCUMENT_CLEARANCE",
              "ACCUMULATE_THEN_FLUSH",
              "PERIODIC_EXPORT"
            ]
          },
          "provenance": {
            "type": "object",
            "description": "Why this transaction resolved the way it did — the matched compliance-mandate rule and when.",
            "properties": {
              "matchedRuleId": {
                "type": "string",
                "nullable": true
              },
              "matchedRuleVersion": {
                "type": "string",
                "nullable": true
              },
              "resolvedAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "matchedRule": {
                "type": "object",
                "nullable": true,
                "properties": {
                  "country": {
                    "type": "string"
                  },
                  "movement": {
                    "type": "string",
                    "nullable": true
                  },
                  "customerType": {
                    "type": "string",
                    "nullable": true
                  },
                  "description": {
                    "type": "string",
                    "nullable": true
                  }
                }
              }
            }
          },
          "period": {
            "type": "object",
            "nullable": true,
            "description": "The reporting period this transaction belongs to — only for accumulate-class mandates.",
            "properties": {
              "id": {
                "type": "string"
              },
              "periodKey": {
                "type": "string"
              },
              "periodStart": {
                "type": "string",
                "format": "date",
                "nullable": true
              },
              "periodEnd": {
                "type": "string",
                "format": "date",
                "nullable": true
              },
              "status": {
                "type": "string",
                "description": "open | closed | submitted | filed | needs_rectification (see the period lifecycle)."
              },
              "closedAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "submittedAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "filedAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "latestSubmissionAttempt": {
                "type": "object",
                "nullable": true,
                "properties": {
                  "status": {
                    "type": "string"
                  },
                  "receiptReference": {
                    "type": "string",
                    "nullable": true
                  },
                  "error": {
                    "type": "string",
                    "nullable": true
                  },
                  "createdAt": {
                    "type": "string",
                    "format": "date-time",
                    "nullable": true
                  }
                }
              }
            }
          },
          "terminalArtifact": {
            "nullable": true,
            "description": "What this transaction's obligation ultimately produced, discriminated by `type`: an e-invoice clearance record ({type: 'einvoice', einvoicingRecordId, clearanceStatus, clearanceStatusLabel, terminal, referenceId, ksefNumber, clearedAt, rejectedAt}), membership of an aggregate reporting period ({type: 'period', periodId, periodKey, periodStatus, closedAt, submittedAt, filedAt}), or {type: 'recorded_only'} when no obligation applies. Follows the resolved mandate class — an accumulated e-reporting transaction always reports its period artifact, never a sibling e-invoice record.",
            "oneOf": [
              {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": [
                      "einvoice"
                    ]
                  },
                  "einvoicingRecordId": {
                    "type": "string"
                  },
                  "clearanceStatus": {
                    "type": "string"
                  },
                  "clearanceStatusLabel": {
                    "type": "string"
                  },
                  "terminal": {
                    "type": "boolean"
                  },
                  "referenceId": {
                    "type": "string",
                    "nullable": true
                  },
                  "ksefNumber": {
                    "type": "string",
                    "nullable": true
                  },
                  "clearedAt": {
                    "type": "string",
                    "format": "date-time",
                    "nullable": true
                  },
                  "rejectedAt": {
                    "type": "string",
                    "format": "date-time",
                    "nullable": true
                  }
                }
              },
              {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": [
                      "period"
                    ]
                  },
                  "periodId": {
                    "type": "string"
                  },
                  "periodKey": {
                    "type": "string"
                  },
                  "periodStatus": {
                    "type": "string"
                  },
                  "closedAt": {
                    "type": "string",
                    "format": "date-time",
                    "nullable": true
                  },
                  "submittedAt": {
                    "type": "string",
                    "format": "date-time",
                    "nullable": true
                  },
                  "filedAt": {
                    "type": "string",
                    "format": "date-time",
                    "nullable": true
                  }
                }
              },
              {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": [
                      "recorded_only"
                    ]
                  }
                }
              }
            ]
          }
        }
      },
      "StatusResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "referenceId": {
            "type": "string"
          },
          "country": {
            "type": "string"
          },
          "clearanceStatus": {
            "$ref": "#/components/schemas/ClearanceStatus"
          },
          "statusClass": { "$ref": "#/components/schemas/StatusClass" },
          "nextStep": { "$ref": "#/components/schemas/NextStep" },
          "terminal": {
            "type": "boolean"
          },
          "submittedAt": {
            "type": "string",
            "format": "date-time"
          },
          "acceptedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "rejectedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "rejectionReason": {
            "type": "string",
            "nullable": true
          },
          "nextPollAfter": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "sandbox": {
            "type": "boolean"
          }
        }
      },
      "InboundValidation": {
        "type": "object",
        "nullable": true,
        "description": "What was checked on a received document and what was found. States the rule set and version checked against; it never says a document is valid, compliant, approved or rejected. Null when no checks apply to the row.",
        "properties": {
          "outcome": {
            "type": "string",
            "nullable": true,
            "description": "The row's validationOutcome."
          },
          "status": {
            "type": "string",
            "nullable": true,
            "enum": [
              "CHECKS_PASSED",
              "CHECKS_WARNINGS",
              "CHECKS_FAILED",
              "CHECKS_NOT_RUN",
              null
            ]
          },
          "summary": {
            "type": "string",
            "nullable": true,
            "description": "For example \"Checked against Peppol BIS Billing 3.0 (PEPPOL-EN16931-UBL 3.0.21): 0 errors, 2 warnings\"."
          },
          "ruleset": {
            "type": "string",
            "nullable": true,
            "enum": [
              "EN16931",
              "PEPPOL",
              "PEPPOL-SB",
              "XRECHNUNG",
              null
            ]
          },
          "artefact": {
            "type": "object",
            "nullable": true,
            "properties": {
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "checkedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "counts": {
            "type": "object",
            "nullable": true,
            "properties": {
              "errors": {
                "type": "integer"
              },
              "warnings": {
                "type": "integer"
              }
            }
          },
          "findings": {
            "type": "array",
            "description": "Populated on GET /invoices/{id} and the webhook; GET /invoices leaves it empty.",
            "items": {
              "type": "object",
              "properties": {
                "rule": {
                  "type": "string"
                },
                "severity": {
                  "type": "string",
                  "enum": [
                    "error",
                    "warning"
                  ]
                },
                "message": {
                  "type": "string"
                },
                "bmfClass": {
                  "type": "string",
                  "enum": [
                    "FORMAT",
                    "CONTENT",
                    "BUSINESS_RULE"
                  ]
                },
                "location": {
                  "type": "string"
                }
              }
            }
          },
          "truncated": {
            "type": "boolean"
          }
        }
      },
      "InboundAttachment": {
        "type": "object",
        "description": "A supporting document the supplier attached to a received invoice (EN 16931 BG-24), listed only. The bytes are never included and an attachment is never presented as the invoice.",
        "properties": {
          "id": {
            "type": "string",
            "nullable": true
          },
          "fileName": {
            "type": "string",
            "nullable": true
          },
          "mimeType": {
            "type": "string",
            "nullable": true
          },
          "sizeBytes": {
            "type": "integer",
            "nullable": true
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "externalUrl": {
            "type": "string",
            "nullable": true
          },
          "origin": {
            "type": "string",
            "enum": [
              "supplier"
            ]
          }
        }
      },
      "InvoiceDocumentLinks": {
        "type": "object",
        "description": "Generalized download links for the submitted document — every country, resolved automatically on GET /invoices and GET /invoices/{id}.",
        "properties": {
          "xmlUrl": {
            "type": "string",
            "format": "uri",
            "description": "GET this URL (or GET /documents/{id}?format=xml directly) to retrieve the raw XML submitted to the authority."
          },
          "pdfUrl": {
            "type": "string",
            "format": "uri",
            "description": "A readable PDF rendered from the invoice's own stored data. Omitted when GET /documents/{id}?format=pdf would answer 422 — an invoice we issued for IL/JO/EG (the renderer is Latin-script only and hasn't been verified for those scripts yet), or a record without enough stored data to draw; use format=xml/json instead. For a received invoice the PDF is the archived original when one exists, otherwise a labelled reader copy (see `rendition`)."
          }
        },
        "required": [
          "xmlUrl"
        ]
      },
      "InvoiceListItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "referenceId": {
            "type": "string"
          },
          "country": {
            "type": "string"
          },
          "direction": {
            "type": "string",
            "enum": [
              "outbound",
              "inbound"
            ],
            "description": "\"outbound\" = you submitted this invoice, \"inbound\" = you received it from a supplier (Peppol, Poland KSeF via POST /pl/inbound/poll, or France via POST /fr/inbound/poll)."
          },
          "clearanceStatus": {
            "$ref": "#/components/schemas/ClearanceStatus"
          },
          "clearanceStatusLabel": {
            "type": "string",
            "description": "Ready-to-render display label for `clearanceStatus`, e.g. \"Pending\", \"Accepted\", \"Delivered\". For a REJECTED invoice with rejectionSource=PRE_SUBMISSION_CHECK, this reads \"Failed validation\" instead of \"Rejected\" — the invoice never reached the authority, so a plain \"Rejected\" would misleadingly suggest an authority-level rejection.",
            "example": "Delivered"
          },
          "rejectionSource": {
            "type": "string",
            "nullable": true,
            "enum": [
              "PRE_SUBMISSION_CHECK",
              "AUTHORITY"
            ],
            "description": "Only meaningful when clearanceStatus=REJECTED. PRE_SUBMISSION_CHECK means our own pre-submission validation caught a problem and the authority was never called; AUTHORITY means the authority itself received and rejected it. Null for every non-REJECTED status."
          },
          "terminal": {
            "type": "boolean"
          },
          "currency": {
            "type": "string"
          },
          "totalAmount": {
            "type": "number"
          },
          "submittedAt": {
            "type": "string",
            "format": "date-time"
          },
          "intakeChannel": {
            "type": "string",
            "nullable": true,
            "description": "Received documents only (direction inbound). Which intake adapter delivered the document: PEPPOL_AS4 (the Peppol network), DE_INBOUND_API, DE_INBOUND_DASHBOARD, DE_INBOUND_BULK or DE_INBOUND_EMAIL."
          },
          "source": {
            "type": "object",
            "description": "How the record entered the platform (ingestion channel): for example api (a direct API call), mcp (sent through an MCP tool), dashboard, batch_upload, or the key of a connected integration. Set by the platform from how the request arrived; it cannot be supplied in a request.",
            "properties": {
              "value": {
                "type": "string",
                "nullable": true,
                "description": "Machine-readable channel key; null when no channel was recorded."
              },
              "label": {
                "type": "string",
                "description": "Ready-to-render label (for example API, MCP, Dashboard, Batch Upload); 'Unknown' when the channel is not recorded."
              }
            }
          },
          "receivedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Received documents only. When the platform actually received the document (not when it was stored)."
          },
          "validation": {
            "allOf": [
              {
                "$ref": "#/components/schemas/InboundValidation"
              }
            ],
            "nullable": true,
            "description": "Received documents only. What was checked and found — rule set, version, error and warning counts, and (on GET /invoices/{id} only) the individual findings."
          },
          "attachments": {
            "type": "array",
            "nullable": true,
            "items": {
              "$ref": "#/components/schemas/InboundAttachment"
            },
            "description": "Received documents only. The supplier's own supporting documents, listed only — never the bytes. Null when there are none."
          },
          "rendition": {
            "type": "string",
            "nullable": true,
            "enum": [
              "original",
              "generated",
              "none",
              null
            ],
            "description": "Received documents only. How GET /documents/{id}?format=pdf answers: original (the archived PDF, byte for byte), generated (a labelled readable copy rendered from the structured data — a reader copy, not an invoice in its own right; this covers any received Peppol invoice), or none (it would answer 422)."
          },
          "supersededBy": {
            "type": "object",
            "nullable": true,
            "properties": {
              "id": {
                "type": "string"
              }
            },
            "description": "Received documents only. The corrected re-send that replaced this needs-review record. The full reference is on GET /invoices/{id}."
          },
          "documents": {
            "$ref": "#/components/schemas/InvoiceDocumentLinks"
          },
          "documentType": {
            "type": "string",
            "description": "invoice, credit_note or debit_note (as stored)."
          },
          "inputFormat": {
            "type": "string",
            "nullable": true,
            "description": "Format the document was submitted in."
          },
          "ksefNumber": {
            "type": "string",
            "nullable": true,
            "description": "Poland KSeF reference number, when assigned."
          },
          "sessionId": {
            "type": "string",
            "nullable": true,
            "description": "Authority session identifier, when applicable."
          },
          "entityId": {
            "type": "string"
          },
          "transactionId": {
            "type": "string",
            "nullable": true,
            "description": "Parent compliance-mandate transaction ID (see GET /mandate-transactions/{id}); null until one is resolved."
          },
          "customerReference": {
            "type": "string",
            "nullable": true
          },
          "fileName": {
            "type": "string",
            "nullable": true
          },
          "invoiceNumber": {
            "type": "string",
            "nullable": true
          },
          "supplierName": {
            "type": "string",
            "nullable": true
          },
          "customerName": {
            "type": "string",
            "nullable": true
          },
          "customerTaxId": {
            "type": "string",
            "nullable": true
          },
          "clearedAt": {
            "type": "string",
            "nullable": true,
            "format": "date-time"
          },
          "rejectedAt": {
            "type": "string",
            "nullable": true,
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "mcDeadline": {
            "type": "string",
            "nullable": true,
            "description": "Italy: statutory deadline for the customer-channel-unreachable (MC) retry window.",
            "format": "date-time"
          },
          "excludedByRule": {
            "type": "string",
            "nullable": true,
            "description": "Code of the rule that excluded this record (EXCLUDED status); null otherwise."
          },
          "excludedMatchedList": {
            "type": "string",
            "nullable": true,
            "description": "Set only for a customer-exclusion rule match: which list matched.",
            "enum": [
              "taxId",
              "customerRef",
              "name",
              null
            ]
          },
          "excludedMatchedValue": {
            "type": "string",
            "nullable": true,
            "description": "Set only for a customer-exclusion rule match: the exact value that matched."
          },
          "erpSource": {
            "type": "object",
            "nullable": true,
            "description": "Provenance summary for a record synced from an ERP integration (e.g. Zoho Books); null otherwise. Full detail is on GET /invoices/{id}.",
            "properties": {
              "system": {
                "type": "string"
              },
              "organizationId": {
                "type": "string",
                "nullable": true
              },
              "dcRegion": {
                "type": "string",
                "nullable": true
              },
              "resourceType": {
                "type": "string",
                "nullable": true
              },
              "externalId": {
                "type": "string",
                "nullable": true
              },
              "externalNumber": {
                "type": "string",
                "nullable": true
              }
            }
          },
          "businessStatus": {
            "type": "string",
            "nullable": true,
            "description": "The record's own native business status (e.g. the shared FR/DE 9-state vocabulary, or Brazil's own)."
          },
          "businessStatusLabel": {
            "type": "string",
            "nullable": true,
            "description": "Ready-to-render label for businessStatus."
          },
          "canonicalBusinessStatus": {
            "type": "string",
            "nullable": true,
            "description": "Regime-agnostic equivalent of businessStatus (RECEIVED, ACKNOWLEDGED, ACCEPTED or DISPUTED); null when the record's country has no business-status regime."
          },
          "businessStatusActions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BusinessStatusAction"
            },
            "description": "Inbound records only: the next business-status actions currently available (see PATCH /invoices/{id}/business-status). Absent for outbound rows and for countries with no business-status regime."
          },
          "businessStatusUnavailableReason": {
            "type": "string",
            "description": "Present when businessStatusActions is empty for a reason worth showing (e.g. a cancelled or denied Brazilian document)."
          },
          "documentFamily": {
            "type": "string",
            "nullable": true,
            "description": "Brazil rows only: nfe (goods) or nfse (services).",
            "enum": [
              "nfe",
              "nfse",
              null
            ]
          },
          "documentFamilyLabel": {
            "type": "string",
            "nullable": true,
            "description": "Brazil rows only."
          },
          "pdfUrl": {
            "type": "string",
            "nullable": true,
            "description": "Brazil rows only: relative DANFSe download link for NFS-e rows; null for NF-e."
          },
          "errorCode": {
            "type": "string",
            "nullable": true,
            "description": "Only when status=failed."
          },
          "isMerchantResolvable": {
            "type": "boolean",
            "description": "Only when status=failed."
          },
          "suggestedAction": {
            "type": "string",
            "nullable": true,
            "description": "Only when status=failed: detail of the most recent rejection/undelivered event, or the upstream code."
          }
        }
      },
      "InvoiceDetail": {
        "type": "object",
        "description": "Full detail for a single submitted invoice. Superset of InvoiceListItem.",
        "properties": {
          "id": {
            "type": "string"
          },
          "referenceId": {
            "type": "string",
            "nullable": true
          },
          "country": {
            "type": "string"
          },
          "documentType": {
            "type": "string",
            "enum": [
              "invoice",
              "credit_note",
              "debit_note"
            ]
          },
          "direction": {
            "type": "string",
            "enum": [
              "outbound",
              "inbound"
            ]
          },
          "clearanceStatus": {
            "$ref": "#/components/schemas/ClearanceStatus"
          },
          "clearanceStatusLabel": {
            "type": "string",
            "description": "Ready-to-render display label for `clearanceStatus`, e.g. \"Pending\", \"Accepted\", \"Delivered\". For a REJECTED invoice with rejectionSource=PRE_SUBMISSION_CHECK, this reads \"Failed validation\" instead of \"Rejected\" — the invoice never reached the authority, so a plain \"Rejected\" would misleadingly suggest an authority-level rejection.",
            "example": "Delivered"
          },
          "rejectionSource": {
            "type": "string",
            "nullable": true,
            "enum": [
              "PRE_SUBMISSION_CHECK",
              "AUTHORITY"
            ],
            "description": "Only meaningful when clearanceStatus=REJECTED. PRE_SUBMISSION_CHECK means our own pre-submission validation caught a problem and the authority was never called; AUTHORITY means the authority itself received and rejected it. Null for every non-REJECTED status."
          },
          "businessStatus": {
            "type": "string",
            "nullable": true,
            "enum": [
              "IN_HAND",
              "APPROVED",
              "PARTIALLY_APPROVED",
              "DISPUTED",
              "SUSPENDED",
              "REFUSED",
              "COMPLETED",
              "PAYMENT_SENT",
              "PAYMENT_RECEIVED",
              "CIENCIA",
              "CONFIRMACAO",
              "OPERACAO_NAO_REALIZADA",
              "DESCONHECIMENTO",
              "PRESUMIDA",
              null
            ],
            "description": "The record's own native post-clearance business status — the shared FR/DE vocabulary, or Brazil's Manifestação do Destinatário values — see PATCH /invoices/{id}/business-status. Null for a record with no business status yet (a fresh Brazilian inbound row reads as awaiting response) and for countries without a business-status regime."
          },
          "statusClass": { "$ref": "#/components/schemas/StatusClass" },
          "nextStep": { "$ref": "#/components/schemas/NextStep" },
          "terminal": {
            "type": "boolean"
          },
          "entityId": {
            "type": "string"
          },
          "customerReference": {
            "type": "string",
            "nullable": true
          },
          "invoiceNumber": {
            "type": "string",
            "nullable": true
          },
          "currency": {
            "type": "string",
            "nullable": true
          },
          "totalAmount": {
            "type": "number",
            "nullable": true
          },
          "totalNet": {
            "type": "number",
            "nullable": true
          },
          "totalTax": {
            "type": "number",
            "nullable": true,
            "description": "Total tax on the document (tax-type-neutral; formerly `totalVat`)."
          },
          "totalGross": {
            "type": "number",
            "nullable": true
          },
          "payableAmount": {
            "type": "number",
            "nullable": true
          },
          "issueDate": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "dueDate": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "supplierName": {
            "type": "string",
            "nullable": true
          },
          "supplierTaxId": {
            "type": "string",
            "nullable": true
          },
          "supplierLegalRegistrationId": {
            "type": "string",
            "nullable": true,
            "description": "BT-30. Germany only today (invoice-content-field-registry) — the seller's Handelsregisternummer (e.g. \"HRB 12345\"), when its legal form is Handelsregister-registered and the value was resolved (per-invoice override or the entity's own DE registration). Null for every other country, and for a DE seller with no Handelsregister-registered legal form declared."
          },
          "supplierAdditionalLegalInfo": {
            "type": "string",
            "nullable": true,
            "description": "BT-33. Germany only today — the assembled company-law disclosure string (register court, seat, managing-director names, as applicable to the seller's declared legal form) rendered verbatim on the XML/PDF/hosted view. Null for every other country and for a DE seller with nothing to disclose."
          },
          "customerName": {
            "type": "string",
            "nullable": true
          },
          "customerTaxId": {
            "type": "string",
            "nullable": true
          },
          "submittedAt": {
            "type": "string",
            "format": "date-time"
          },
          "clearedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "rejectedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "authorityRefs": {
            "type": "array",
            "description": "Country-specific identifiers assigned by the tax authority (SDI ID, KSeF number, NAV ID, etc.).",
            "items": {
              "type": "object",
              "properties": {
                "label": {
                  "type": "string",
                  "description": "Identifier type (e.g. sdIId, ksefNumber, navId)"
                },
                "value": {
                  "type": "string"
                }
              }
            }
          },
          "upstreamCode": {
            "type": "string",
            "nullable": true,
            "description": "Error or status code returned by the tax authority."
          },
          "upstreamMessage": {
            "type": "string",
            "nullable": true,
            "description": "Human-readable message from the tax authority."
          },
          "suggestedAction": {
            "type": "string",
            "nullable": true,
            "description": "Guidance derived from the authority rejection detail."
          },
          "events": {
            "type": "array",
            "description": "Chronological log of authority events for this invoice.",
            "items": {
              "type": "object",
              "properties": {
                "at": {
                  "type": "string",
                  "format": "date-time"
                },
                "event": {
                  "type": "string"
                },
                "detail": {
                  "type": "string",
                  "nullable": true
                }
              }
            }
          },
          "submittedXml": {
            "type": "string",
            "nullable": true,
            "description": "The raw XML submitted to the tax authority."
          },
          "inputFormat": {
            "type": "string",
            "enum": [
              "NATIVE_XML",
              "UNIVERSAL_JSON"
            ],
            "description": "Whether this invoice's content came from Clearvo's own JSON invoice shape (UNIVERSAL_JSON — the only format POST /send accepts) or was stored with no CDM behind it (NATIVE_XML — always true for a received/inbound document, e.g. Poland KSeF, France, Peppol AS4, Mexico CFDI, Brazil NF-e; POST /send no longer accepts a pre-built XML submission). Determines whether `input` below is populated."
          },
          "ksefNumber": {
            "type": "string",
            "nullable": true,
            "description": "Poland KSeF reference number, when assigned. Also surfaced generically in `authorityRefs`; kept here as its own field for backward-compatible direct access."
          },
          "sessionId": {
            "type": "string",
            "nullable": true,
            "description": "Internal batch/session correlation identifier, when this invoice was submitted as part of a batch. Opaque — has no meaning outside Clearvo's own systems."
          },
          "fileName": {
            "type": "string",
            "nullable": true,
            "description": "The generated filename for the submitted document (e.g. the Italian SDI transmission filename convention), when the country's format prescribes one."
          },
          "mcDeadline": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Italy only: the statutory deadline by which SDI must confirm delivery (Mancata Consegna) before the invoice is deemed accepted regardless."
          },
          "deadlineAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "The deadline by which a HELD_AWAITING_AUTHORIZATION invoice must clear a platform gate (e.g. Italy's SDI intermediary accreditation) before liability shifts to the merchant — see lateTransmissionNotice."
          },
          "lateTransmissionNotice": {
            "type": "string",
            "nullable": true,
            "description": "Merchant-facing liability notice shown once deadlineAt has passed for a still-held invoice."
          },
          "arCaeExpiry": {
            "type": "string",
            "nullable": true,
            "description": "Argentina only: the AFIP-issued CAE (Código de Autorización Electrónico) expiry date for this invoice."
          },
          "rejectionDetail": {
            "nullable": true,
            "allOf": [
              {
                "$ref": "#/components/schemas/RejectionDetail"
              }
            ],
            "description": "Structured, machine-readable rejection detail. Only non-null when clearanceStatus is REJECTED and a code or fallback suggestedAction is available."
          },
          "adjustments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AdjustmentAuditEntry"
            },
            "description": "Append-only audit trail of every ADJUSTMENT (auto-correction) applied to this invoice. Always [] for a record with none."
          },
          "corrections": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CorrectionSummary"
            },
            "description": "Machine-readable superset of `adjustments` for integrator reconciliation — stable ruleCode/ruleVersion/before/after plus tier and a server-generated plain-English description. Same shape POST /v1/send returns synchronously; this is the same data read back later."
          },
          "treatmentChanged": {
            "type": "boolean",
            "description": "True when any applied adjustment's own rule category is TAX_TREATMENT_CHANGE — a real change to what tax treatment was attested to the authority (e.g. the EU-purchase-reverse-charge template), not just a data-hygiene cleanup."
          },
          "excludedByRule": {
            "type": "string",
            "nullable": true,
            "description": "The excluding rule's identity, when clearanceStatus is EXCLUDED. Null for every non-excluded record."
          },
          "excludedMatchedList": {
            "type": "string",
            "nullable": true,
            "enum": [
              "taxId",
              "customerRef",
              "name"
            ],
            "description": "Set only for a customer-exclusion rule (CUSTOMER_EXCLUSION_MATCH) match — which of the rule's configured lists matched. Null for every other EXCLUDE condition type."
          },
          "excludedMatchedValue": {
            "type": "string",
            "nullable": true,
            "description": "The exact value that matched, alongside excludedMatchedList."
          },
          "exclusions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ExclusionAuditEntry"
            },
            "description": "Append-only audit trail of every EXCLUDE (skip) outcome recorded against this invoice. Always [] for a record that was never excluded."
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WarningAuditEntry"
            },
            "description": "Append-only audit trail of every non-blocking WARNING annotation this invoice has ever surfaced — the same warnings POST /v1/send returns synchronously at submission time, still readable later from this endpoint. Always [] for a record with none."
          },
          "enrichments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EnrichmentAuditEntry"
            },
            "description": "Append-only audit trail of every field Clearvo's own pipeline filled in because it was left blank on submission (e.g. a line's taxAmount derived from taxRate, or a country-specific default pulled from the entity's own registration data) — never a value the caller actually supplied (that is an adjustment, not an enrichment). Always [] for a record with none."
          },
          "errorCode": {
            "type": "string",
            "description": "Machine-readable error code from the authority or platform. Only present when the invoice has an associated error."
          },
          "isMerchantResolvable": {
            "type": "boolean",
            "description": "True when the merchant can fix and resubmit without support intervention. Only present alongside errorCode."
          },
          "fixUrl": {
            "type": "string",
            "description": "Dashboard deep link for resolving the flagged issue, when one exists. Only present alongside errorCode, and only when the underlying check has a genuine fix destination."
          },
          "input": {
            "type": "object",
            "additionalProperties": true,
            "description": "The original CustomerInvoiceInput JSON body as submitted, verbatim. Only present when inputFormat is UNIVERSAL_JSON — a NATIVE_XML (received/inbound) record has no equivalent structured input to return."
          },
          "corrects": {
            "nullable": true,
            "allOf": [
              {
                "$ref": "#/components/schemas/CorrectionRefWithRelation"
              }
            ],
            "description": "What this invoice itself corrects, if it is a resubmission or credit note against an earlier one. Null for an original invoice that corrects nothing."
          },
          "correctedBy": {
            "nullable": true,
            "allOf": [
              {
                "$ref": "#/components/schemas/CorrectionRef"
              }
            ],
            "description": "The most recent resubmission pointing at this invoice, active or terminal. Null if this invoice has never been resubmitted."
          },
          "supersededBy": {
            "nullable": true,
            "allOf": [
              {
                "$ref": "#/components/schemas/CorrectionRef"
              }
            ],
            "description": "Same row as correctedBy, but only populated once that resubmission actually succeeded (ACCEPTED/DELIVERED) — a permanently superseded original. correctedBy can be non-null with supersededBy still null while a resubmission is in flight but hasn't succeeded yet. For a received document that failed its checks (needs review), it is the corrected re-send that replaced it."
          },
          "creditNotes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CreditNoteRef"
            },
            "description": "Every credit note issued against this invoice, oldest first."
          },
          "remainingCreditableAmount": {
            "type": "number",
            "nullable": true,
            "description": "Net amount still available to credit against this invoice, after subtracting every prior non-REJECTED credit note already issued against it. Null when the original invoice's own line data isn't available to recompute a total from (e.g. a NATIVE_XML received/inbound record) — never treat null as zero."
          },
          "availableActions": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "retry",
                "fix_and_resubmit",
                "issue_credit_note"
              ]
            },
            "description": "Which correction actions this specific invoice currently supports, given its country, clearance status, and correction history."
          },
          "fixAndResubmitWindowClosed": {
            "type": "boolean",
            "description": "True only when fix_and_resubmit would otherwise be offered but the jurisdiction's own correction-window policy (e.g. Italy's 5-day scarto window) has since closed — never true for any other reason the action might be absent (an ACCEPTED invoice, a credit/debit note, an in-flight correction, a read-only key, NATIVE_XML input). Distinguishes \"was available, window closed\" from \"never applicable\" for a caller building its own UI on top of availableActions."
          },
          "resubmissionNumbering": {
            "nullable": true,
            "allOf": [
              {
                "$ref": "#/components/schemas/ResubmissionNumberingHint"
              }
            ],
            "description": "Numbering rule a Fix & Resubmit call against this invoice must follow, when fix_and_resubmit is an available action. Null otherwise."
          },
          "erpSource": {
            "nullable": true,
            "allOf": [
              {
                "$ref": "#/components/schemas/ErpSourceDetail"
              }
            ],
            "description": "Provenance for a document synced in from a pull-based ERP integration (e.g. Zoho Books). Null for every record not synced from an ERP — a direct API call, a Peppol inbound document, etc."
          },
          "customerNotification": {
            "type": "object",
            "description": "Current-state view of the customer invoice notification feature (see notifyCustomer on POST /send) — distinct from that endpoint's own one-shot customerNotification action result. eligible is false for a country/routing where an authority network already delivers the invoice (Peppol, IT B2B/B2G, PL when the customer has a NIP), or that is a pure background report to the tax authority with no customer-delivery role of its own (PT, HU, GR, AR, JO, and ES while its SII regime is active — ES is eligible when its VeriFactu regime is active instead, since a VeriFactu record is the invoice itself, not a separate report about one) — this feature never applies in those cases. status is null when eligible but never attempted (a SKIPPED outcome, e.g. NOT_OPTED_IN or MISSING_CUSTOMER_EMAIL, is never persisted, so that case is indistinguishable from \"never tried\"). BOUNCED/COMPLAINED are reported asynchronously by the email provider, sometime after SENT — a real bounce (bad address, full mailbox) or spam complaint can arrive well after this endpoint was first checked, so poll again rather than assuming SENT is final.",
            "properties": {
              "eligible": {
                "type": "boolean"
              },
              "status": {
                "type": "string",
                "nullable": true,
                "enum": [
                  "SENT",
                  "FAILED",
                  "VIEWED",
                  "DOWNLOADED",
                  "BOUNCED",
                  "COMPLAINED"
                ]
              },
              "sentAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "viewedAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "downloadedAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "bouncedAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true,
                "description": "When the email provider reported a permanent delivery failure. Null unless status is BOUNCED."
              },
              "complainedAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true,
                "description": "When the recipient marked the email as spam. Null unless status is COMPLAINED."
              },
              "errorMessage": {
                "type": "string",
                "nullable": true,
                "description": "Present when status is FAILED (the synchronous send-time error), or BOUNCED/COMPLAINED (the provider's bounce/complaint reason)."
              }
            }
          },
          "verificationQr": {
            "type": "object",
            "nullable": true,
            "description": "ES (VeriFactu) / PT (AT) only — the country-mandated verification QR, rendered server-side and identical to what's embedded in the issued PDF (GET .../documents/{id}?format=pdf) and the customer-facing hosted page. Null for every other country, and for a record issued before this field existed.",
            "properties": {
              "dataUrl": {
                "type": "string",
                "description": "PNG data URL (data:image/png;base64,...)."
              },
              "legend": {
                "type": "string",
                "description": "Caption printed under the QR — \"VERI*FACTU\" for ES, the ATCUD string for PT."
              }
            }
          },
          "siiDetail": {
            "type": "object",
            "nullable": true,
            "description": "Spain SII audit detail — AEAT's own answer for this registro. Null for every non-SII record (a VeriFactu ES record, or any other country). estado stays PENDING and csv null until AEAT has actually answered; a REJECTED SII record keeps estado Incorrecto with errorCode set.",
            "properties": {
              "estado": {
                "type": "string",
                "enum": [
                  "PENDING",
                  "Correcto",
                  "AceptadoConErrores",
                  "Incorrecto"
                ],
                "description": "AEAT EstadoRegistro for this invoice — PENDING until submitted; Correcto (accepted), AceptadoConErrores (registered by AEAT but with an admissible error that needs an A1 amendment), Incorrecto (rejected)."
              },
              "csv": {
                "type": "string",
                "nullable": true,
                "description": "AEAT Código Seguro de Verificación — the proof of registration. Null until AEAT accepts (Correcto or AceptadoConErrores)."
              },
              "admissibleErrors": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": true
                },
                "description": "For AceptadoConErrores: the AEAT error(s) AEAT accepted the invoice despite — each with its code, category and description. Empty otherwise."
              },
              "errorCode": {
                "type": "string",
                "nullable": true,
                "description": "Top-level AEAT error code for an Incorrecto (rejected) registro, or the admissible-error code for AceptadoConErrores. Null on Correcto/PENDING."
              },
              "xml": {
                "type": "string",
                "nullable": true,
                "description": "The SuministroLRFacturasEmitidas SOAP envelope sent to AEAT (may be omitted/null for very large payloads)."
              },
              "matchedRuleId": {
                "type": "string",
                "nullable": true,
                "description": "The compliance-mandate rule that resolved this transaction to ES_SII — same value as provenance.matchedRuleId on GET /v1/mandate-transactions."
              },
              "createdAt": {
                "type": "string",
                "format": "date-time"
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "sandbox": {
            "type": "boolean",
            "description": "True when this record was created with a test key (csk_test_). A sandbox record is generated and chained locally and is never sent to an authority, so its ACCEPTED status is not an authority's answer."
          },
          "verifactuDetail": {
            "type": "object",
            "nullable": true,
            "description": "Spain VeriFactu only — AEAT's own answer to the real-time submission of this record. Null until AEAT answers, for every non-VeriFactu record, and always in sandbox (which never calls AEAT). clearanceStatus stays ACCEPTED for a generated record, so for an estadoRegistro of AceptadoConErrores or Incorrecto show `statusLabel` and `action` instead of plain Accepted.",
            "properties": {
              "estadoEnvio": {
                "type": "string",
                "description": "AEAT EstadoEnvio for the submission (Correcto, ParcialmenteCorrecto, Incorrecto)."
              },
              "estadoRegistro": {
                "type": "string",
                "enum": [
                  "Correcto",
                  "AceptadoConErrores",
                  "Incorrecto"
                ],
                "description": "AEAT EstadoRegistro for this record."
              },
              "statusLabel": {
                "type": "string",
                "description": "Ready-to-render label, e.g. 'Rejected by AEAT'."
              },
              "action": {
                "type": "string",
                "nullable": true,
                "description": "What the customer should do; null when nothing is needed."
              },
              "csv": {
                "type": "string",
                "nullable": true,
                "description": "AEAT Código Seguro de Verificación."
              },
              "codigoErrorRegistro": {
                "type": "string",
                "nullable": true
              },
              "descripcionErrorRegistro": {
                "type": "string",
                "nullable": true
              },
              "answeredAt": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "verifactuSubmission": {
            "type": "object",
            "nullable": true,
            "description": "Spain VeriFactu only — set when AEAT never gave a record-level answer (verifactuDetail is then null). `UNKNOWN`: no readable response came back after the retries, so AEAT may or may not hold the record. `SOAP_FAULT`: AEAT answered with a service fault and did not process it. clearanceStatus stays ACCEPTED for a generated record, so show `statusLabel` and `action`, never plain Accepted. Null otherwise.",
            "properties": {
              "outcome": {
                "type": "string",
                "enum": [
                  "UNKNOWN",
                  "SOAP_FAULT"
                ]
              },
              "statusLabel": {
                "type": "string",
                "description": "Ready-to-render label, e.g. 'AEAT confirmation pending'."
              },
              "action": {
                "type": "string",
                "nullable": true,
                "description": "What the customer should do."
              },
              "attempts": {
                "type": "integer",
                "nullable": true
              },
              "at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              }
            }
          },
          "ptReportingDetail": {
            "type": "object",
            "nullable": true,
            "description": "Portugal PT_EFATURA only — the state of this invoice's invoice-data communication to AT (Autoridade Tributária), the reporting obligation an entity takes on by enabling `pt_efatura`. Null for every non-PT_EFATURA record (a PT record issued without pt_efatura enabled, or any other country). Distinct from this same object's clearanceStatus/terminal, which describe the DOCUMENT and stay ACCEPTED/true throughout — a rejected or pending communication never changes them; only this separate obligation needs attention.",
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "PENDING",
                  "ACCEPTED",
                  "REJECTED",
                  "CONFLICT",
                  "HELD_CERTIFICATION",
                  "SUBMITTED_UNKNOWN"
                ],
                "description": "PENDING — queued or awaiting AT (also the resting state while the sub-user webservice credential itself is faulty: fix it via POST /v1/pt/credentials and the communication is retried). ACCEPTED — AT registered the invoice data (fires invoice.reported). REJECTED — AT refused the content (fires invoice.report_rejected). CONFLICT — AT already holds this invoice number under different values; held for human review, never auto-adopted. HELD_CERTIFICATION — not yet ready to call AT live: the country is not yet enabled for your account, or webservice credentials are pending, or this entity has no AT sub-user/password configured yet (POST /v1/pt/credentials). SUBMITTED_UNKNOWN — every delivery attempt ended in a transport failure or an AT retry-later answer, so the outcome is unknown and needs checking."
              },
              "atResponseCode": {
                "type": "string",
                "nullable": true,
                "description": "AT's own response code for the last attempt (e.g. \"0\" accepted). Null until AT has answered."
              },
              "atResponseMessage": {
                "type": "string",
                "nullable": true,
                "description": "Plain-English summary of atResponseCode. Null until AT has answered."
              },
              "communicatedAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true,
                "description": "When AT accepted the communication. Null unless status is ACCEPTED."
              },
              "attempts": {
                "type": "integer",
                "description": "Communication attempts made so far."
              },
              "statusLabel": {
                "type": "string",
                "nullable": true,
                "description": "Backend-authored human label for `status`, ready to render verbatim: \"Reported to AT\" (ACCEPTED), \"Report rejected\" (REJECTED, CONFLICT), \"Not yet reported\" (PENDING, HELD_CERTIFICATION, SUBMITTED_UNKNOWN)."
              },
              "reportedAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true,
                "description": "Alias of communicatedAt — the \"Reported to AT: <date>\" line reads this."
              },
              "suggestedAction": {
                "type": "string",
                "nullable": true,
                "description": "Backend-authored, brand-neutral plain-language next step. Present for REJECTED (explains AT's response code, names the monthly file as the fallback), CONFLICT / SUBMITTED_UNKNOWN / HELD_CERTIFICATION (reassurance — nothing needed from you), and a PENDING communication parked on an invalid sub-user credential (fix it via POST /v1/pt/credentials). Null for ACCEPTED and an ordinary PENDING."
              },
              "actionOwner": {
                "type": "string",
                "nullable": true,
                "enum": [
                  "merchant",
                  "platform"
                ],
                "description": "Who must act on suggestedAction: `merchant` (a content rejection to correct, or the sub-user credential to fix), `platform` (Clearvo is handling it — a certificate fault, a CONFLICT under review, an unconfirmed submission, or the pending platform certification). Null when suggestedAction is null."
              }
            }
          },
          "intakeChannel": {
            "type": "string",
            "nullable": true,
            "description": "Received documents only (direction inbound). Which intake adapter delivered the document: PEPPOL_AS4 (the Peppol network), DE_INBOUND_API, DE_INBOUND_DASHBOARD, DE_INBOUND_BULK or DE_INBOUND_EMAIL."
          },
          "receivedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Received documents only. When the platform actually received the document (not when it was stored)."
          },
          "validation": {
            "allOf": [
              {
                "$ref": "#/components/schemas/InboundValidation"
              }
            ],
            "nullable": true,
            "description": "Received documents only. What was checked and found — rule set, version, error and warning counts and the individual findings. States what was checked; it never says a document is valid or compliant."
          },
          "attachments": {
            "type": "array",
            "nullable": true,
            "items": {
              "$ref": "#/components/schemas/InboundAttachment"
            },
            "description": "Received documents only. The supplier's own supporting documents, listed only — never the bytes. Null when there are none."
          },
          "rendition": {
            "type": "string",
            "nullable": true,
            "enum": [
              "original",
              "generated",
              "none",
              null
            ],
            "description": "Received documents only. How GET /documents/{id}?format=pdf answers: original (the archived PDF, byte for byte), generated (a labelled readable copy rendered from the structured data — a reader copy, not an invoice in its own right; this covers any received Peppol invoice), or none (it would answer 422)."
          },
          "documents": {
            "$ref": "#/components/schemas/InvoiceDocumentLinks"
          },
          "businessStatusLabel": {
            "type": "string",
            "nullable": true,
            "description": "Ready-to-render label for businessStatus."
          },
          "canonicalBusinessStatus": {
            "type": "string",
            "nullable": true,
            "description": "Regime-agnostic equivalent of businessStatus (RECEIVED, ACKNOWLEDGED, ACCEPTED or DISPUTED); null when the record's country has no business-status regime.",
            "enum": [
              "RECEIVED",
              "ACKNOWLEDGED",
              "ACCEPTED",
              "DISPUTED",
              null
            ]
          },
          "businessStatusActions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BusinessStatusAction"
            },
            "description": "Inbound records only: the next business-status actions currently available. Absent for outbound records and countries with no business-status regime."
          },
          "businessStatusUnavailableReason": {
            "type": "string",
            "description": "Present when businessStatusActions is empty because the regime refuses further actions (e.g. a cancelled, denied or deemed-accepted Brazilian document)."
          },
          "transactionId": {
            "type": "string",
            "nullable": true,
            "description": "Parent compliance-mandate transaction ID (see GET /mandate-transactions/{id}); null until one is resolved."
          },
          "lines": {
            "type": "array",
            "description": "Per-line tax-code resolution audit trail, written once at send time and never rewritten. [] for a record with none (a NATIVE_XML request, a credit note, or one predating this field).",
            "items": {
              "type": "object",
              "required": [
                "lineNumber",
                "taxResolution"
              ],
              "properties": {
                "lineNumber": {
                  "type": "integer"
                },
                "taxResolution": {
                  "$ref": "#/components/schemas/LineTaxResolution"
                }
              }
            }
          },
          "originalDocumentUrl": {
            "type": "string",
            "nullable": true,
            "description": "Absolute link to GET /documents/{id}?format=original, the archived original file byte for byte (e.g. a received ZUGFeRD PDF). Null when no original is archived.",
            "format": "uri"
          },
          "respondBy": {
            "type": "string",
            "nullable": true,
            "description": "Brazil only: the end of the statutory day by which a conclusive manifestation must be given (same instant as deadlineAt for a Brazilian row).",
            "format": "date-time"
          },
          "documentStage": {
            "type": "string",
            "nullable": true,
            "description": "Brazil only: 'summary' (only the resNFe summary has arrived) or 'full' (the full document is stored).",
            "enum": [
              "summary",
              "full",
              null
            ]
          },
          "documentFamily": {
            "type": "string",
            "nullable": true,
            "description": "Brazil only: nfe (goods) or nfse (services).",
            "enum": [
              "nfe",
              "nfse",
              null
            ]
          },
          "documentFamilyLabel": {
            "type": "string",
            "nullable": true,
            "description": "Brazil only."
          },
          "pdfUrl": {
            "type": "string",
            "nullable": true,
            "description": "Brazil only: relative DANFSe download link for NFS-e records; null for NF-e."
          },
          "source": {
            "type": "object",
            "description": "How the record entered the platform (ingestion channel).",
            "properties": {
              "value": {
                "type": "string",
                "nullable": true
              },
              "label": {
                "type": "string",
                "description": "Ready-to-render label; 'Unknown' when the channel is not recorded."
              }
            }
          }
        }
      },
      "ItProfileResponse": {
        "type": "object",
        "description": "Italy's Regime Fiscale profile field, current state plus the full option list. Not a credential — Clearvo is the accredited SDI intermediary.",
        "properties": {
          "entityId": {
            "type": "string"
          },
          "hasItRegistration": {
            "type": "boolean",
            "description": "False when the entity has no current Italy tax registration at all — setItProfile is guaranteed to 422 MISSING_IT_REGISTRATION in that state; register one via the Tax Registrations API first."
          },
          "configured": {
            "type": "boolean",
            "description": "True once regimeFiscale is set."
          },
          "regimeFiscale": {
            "type": "string",
            "nullable": true,
            "description": "null until a value has been set."
          },
          "options": {
            "type": "array",
            "description": "The full set of valid Regime Fiscale codes with labels.",
            "items": {
              "type": "object",
              "properties": {
                "value": {
                  "type": "string",
                  "example": "RF01"
                },
                "label": {
                  "type": "string",
                  "example": "Ordinary regime"
                }
              }
            }
          }
        }
      },
      "PtCredentialSeries": {
        "type": "object",
        "description": "The entity's stored Portugal AT document series per document type — FT (invoices), NC (credit notes), ND (debit notes) — null for a type with nothing stored. Derived by the backend from the flat invoiceSeries/creditNoteSeries/debitNoteSeries fields plus each series' provenance; render it rather than reconstructing state from the flat fields.",
        "properties": {
          "FT": {
            "$ref": "#/components/schemas/PtSeriesInfo"
          },
          "NC": {
            "$ref": "#/components/schemas/PtSeriesInfo"
          },
          "ND": {
            "$ref": "#/components/schemas/PtSeriesInfo"
          }
        }
      },
      "PtSeriesInfo": {
        "type": "object",
        "nullable": true,
        "properties": {
          "series": {
            "type": "string",
            "example": "A2026"
          },
          "validationCode": {
            "type": "string",
            "example": "AAJFJ7X5"
          },
          "source": {
            "type": "string",
            "enum": [
              "manual",
              "auto",
              "sandbox"
            ],
            "description": "`manual` — entered by the taxpayer from Portal das Finanças (also every row saved before provenance was recorded); `auto` — Clearvo registered or adopted it with AT (RegistarSerie/ConsultarSeries); `sandbox` — AT's own SANDBOX/\"0\" placeholder written by a sandbox-context save."
          }
        }
      },
      "PtSeriesRegistrationPresentation": {
        "type": "object",
        "nullable": true,
        "description": "One backend-authored status line for the stored series, brand-neutral and ready to render. Null only when no credentials are stored at all.",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "manual",
              "registered",
              "pending_platform_certification",
              "drift",
              "none"
            ],
            "description": "`manual` — the FT series was entered by the taxpayer; `registered` — Clearvo registered/adopted it with AT (or wrote the sandbox placeholder) and no drift is flagged; `pending_platform_certification` — a sub-user is stored with no series yet, registration waits on the platform's own AT certification/production webservice tier (nothing needed from the customer; completed automatically); `drift` — the daily ConsultarSeries check found AT's record no longer matches a stored code (live issuance of that document type holds SETUP_NEEDED PT_SERIES_CODE_MISMATCH until re-checked and re-saved); `none` — nothing stored and no sub-user to register one."
          },
          "message": {
            "type": "string",
            "example": "Series A2026 · entered from Portal das Finanças"
          }
        }
      },
      "FrCapabilityPresentation": {
        "type": "object",
        "description": "Status for one France platform capability (einvoicing or ereporting). Backend-authored label and message — render verbatim, never re-derive from status alone or infer the internal cause.",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "active",
              "pending_activation",
              "sandbox"
            ],
            "description": "`active` — the platform's own France connection is live for this tax number and this capability — for `ereporting`, this means the reporting channel is open, not that sales are already being transmitted: that additionally requires this entity's own fr_ereporting obligation to be enabled (PATCH /v1/tax/reporting-obligations). `pending_activation` — that isn't yet true; nothing is needed from you unless we contact you. `sandbox` — a sandbox API key; sandbox sends work now, regardless of the production verdict either way."
          },
          "label": {
            "type": "string",
            "description": "Plain-language capability name, ready to render.",
            "example": "Sending and receiving invoices"
          },
          "message": {
            "type": "string",
            "description": "Ready-to-render, calm, partner-neutral one-liner for this capability.",
            "example": "Sending and receiving invoices for FR12000000000: pending activation — nothing needed from you unless we contact you."
          },
          "actionOwner": {
            "type": "string",
            "nullable": true,
            "enum": [
              "platform",
              null
            ],
            "description": "Who owes the next step. `platform` while pending and a tax number is registered — there is nothing for you to do; null once active, or when nothing is registered yet (the next step is registering one, not waiting)."
          }
        }
      },
      "FrNextStep": {
        "type": "object",
        "description": "A static follow-on call this response documents, not one it performs itself — registering a French VAT number never flips a reporting obligation on its own.",
        "properties": {
          "action": {
            "type": "string",
            "example": "enable_ereporting"
          },
          "method": {
            "type": "string",
            "example": "PATCH"
          },
          "path": {
            "type": "string",
            "example": "/v1/tax/reporting-obligations"
          },
          "body": {
            "type": "object",
            "description": "The request body shape to send. Any field named in requiredInputs is sent here as `null` — that is not an example value, it is a placeholder for a fact about the entity's own tax position (e.g. the date its French e-reporting obligation applies from under the rollout calendar — fixed by the entity's own size category, or the later date it came into scope, and never a free preference — and its real vatRegime) that the platform has no way to determine on the caller's behalf. Sending the body unedited 400s (e.g. EFFECTIVE_FROM_REQUIRED) by design; replace every requiredInputs field with a real value first."
          },
          "applicableTo": {
            "type": "string",
            "description": "Who this step applies to.",
            "example": "sellers with French B2C or cross-border B2B sales, and possibly established buyers purchasing from suppliers outside France"
          },
          "requiredInputs": {
            "type": "array",
            "description": "Fields inside `body` that are null placeholders, not real values — the platform cannot determine them on the caller's behalf. Omitted when `body` needs no such replacement.",
            "items": {
              "type": "object",
              "properties": {
                "field": {
                  "type": "string",
                  "example": "effectiveFrom"
                },
                "description": {
                  "type": "string"
                },
                "options": {
                  "type": "array",
                  "description": "Present only for a field with a closed set of valid values (e.g. vatRegime).",
                  "items": {
                    "type": "object",
                    "properties": {
                      "value": {
                        "type": "string"
                      },
                      "label": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "WebhookEndpoint": {
        "type": "object",
        "required": [
          "id",
          "url",
          "events"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "example": "https://your-app.com/webhooks/clearvo"
          },
          "statuses": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/ClearanceStatus" },
            "description": "Only applies to `invoice.status_changed`: the statuses this endpoint receives, matched on the new status. Empty means every status. Always returned, as an empty list when unset."
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "*",
                "invoice.received",
                "invoice.status_changed",
                "invoice.accepted",
                "invoice.rejected",
                "invoice.duplicate",
                "invoice.undelivered",
                "invoice.pending",
                "product.classification_changed",
                "held_unmapped_decision",
                "reporting_batch.ready_for_review",
                "reporting_batch.deadline_approaching",
                "reporting_batch.deadline_passed",
                "reporting_batch.overdue_reminder",
                "accepted_with_errors",
                "invoice.amended",
                "invoice.cancelled",
                "invoice.accepted_with_errors",
                "invoice.amend_due",
                "invoice.report_due_soon",
                "invoice.report_overdue",
                "invoice.reported",
                "invoice.report_rejected",
                "invoice.summary_received",
                "invoice.business_status_changed",
                "ap.decision",
                "filing.enabled",
                "filing.ready",
                "filing.approved",
                "filing.paused",
                "filing.resumed",
                "filing.pause_ending",
                "filing.payment_due",
                "filing.submitted",
                "filing.filed",
                "filing.skipped",
                "filing.not_filed",
                "filing.action_needed"
              ],
              "description": "The exact set POST /webhooks accepts — any other value is rejected with 400. `*` subscribes to every event except `invoice.status_changed` (including ones with no individually-subscribable name, e.g. invoice.delivered / invoice.excluded); `invoice.status_changed` must be listed by name. The specific invoice events are frozen and keep their payloads; a webhook subscribed to both a specific event and `invoice.status_changed` receives two deliveries (different delivery ids) for one change. invoice.received fires for an inbound invoice from a supplier — Poland KSeF, France (via the accredited third-party platform), Peppol AS4, DE (POST /v1/receive, POST /v1/receive/bulk, or forwarded to the entity's own inbound email address), or Mexico CFDI (either sat_pull or client_push ingestion). For DE only, the delivered payload carries an additive validationOutcome field — see the `de (invoice.received for a DE inbound document)` entry under x-payloadFields below. For Mexico specifically it fires only for document_type invoice/credit_note (CFDI types I/E — payment receipts and transfers, P/T, are stored silently with no event) and only once SAT's own status check answered Vigente; a CFDI that turns out Cancelado (including one already cancelled before Clearvo could retrieve it) never fires this event. The delivered payload carries an additive `mx` block — see x-payloadFields below. invoice.rejected fires both for an authority clearance rejection and, for France, a customer refusing a received invoice — the delivered payload's rejectedBy field ('authority' or 'customer', was 'buyer' before 2026-09-21; historical deliveries and stored invoice records were rewritten to 'customer') distinguishes the two; subscribe to this one event to see either. held_unmapped_decision fires when mandate resolution found no compliance-mandate rule for a transaction's tax decision (clearanceStatus HELD_UNMAPPED_DECISION) — a platform configuration gap, held and never dispatched until a rule is added; the payload carries holdReason and actionOwner ('platform'). accepted_with_errors is Spain SII's AceptadoConErrores outcome — AEAT registered the invoice (the record's clearanceStatus is ACCEPTED, so an integrator subscribed only to invoice.accepted still sees it) but flagged an admissible error that needs an A1 amendment; it has its own event name so you can subscribe to exactly that without catching every ordinary acceptance. Spain SII block 3: invoice.amended fires when an A1 amendment was accepted by AEAT (Correcto or AceptadoConErrores) — the record's own clearanceStatus stays ACCEPTED; this is the only signal the registered content just changed. invoice.cancelled fires when a Baja was accepted by AEAT (Correcto) — clearanceStatus moves to the new terminal CANCELLED status. The four reporting_batch.* events are the Spain SII reporting-batch reminder ladder (es_sii submissionMode batch_auto/batch_review), computed in Spanish business days off the batch's report-by date and fired at most once per batch per day per stage: reporting_batch.ready_for_review — a batch_review batch has been frozen for your review (exactly once per batch; confirm it via POST /v1/reporting-batches/{id}/confirm); reporting_batch.deadline_approaching — 2 and 1 Spanish business days before the batch's report-by date; reporting_batch.deadline_passed — the first Spanish business day after report-by, or the moment a batch_auto submission gave up after exhausting retries past its deadline; reporting_batch.overdue_reminder — every Spanish business day after that until the batch is submitted. Batch events are never suppressed by a user's email notification preferences — a webhook subscriber is a system integration. invoice.reported / invoice.report_rejected are Portugal PT_EFATURA's invoice-data-communication outcome (the `pt_efatura` reporting obligation): AT accepted, or refused, the communication of an already-issued document's data. They are distinct from invoice.accepted, which fired once at the document's own issuance and never fires again for that record — the record's clearanceStatus stays ACCEPTED/terminal whatever AT answers on the communication, so subscribe to these two to follow the separate reporting obligation (see ptReportingDetail on GET /v1/invoices/{id}). invoice.summary_received is Brazil-only: Distribuição DFe hands the recipient only a resNFe summary (issuer/totals/status, no line items) until the recipient manifests — this event fires once, at that first discovery, distinct from invoice.received (which fires once, later, when the full byte-identical procNFe XML is stored); an integrator that only wants the complete document subscribes to invoice.received unchanged, one that wants earliest-possible visibility (e.g. to start its own Ciência countdown) subscribes to this one too. The delivered payload carries the same additive br block as invoice.received — see the `br` entry under x-payloadFields below. invoice.business_status_changed fires whenever a customer-lifecycle status transition is recorded on a received invoice — via PATCH /v1/invoices/{id}/business-status, or a status the network/authority itself reported back (France's accredited third-party platform sync, Brazil's own auto-Ciência or NSU-stream discovery) — for any registered regime (France, Germany, Brazil). See the `invoice.business_status_changed` entry under x-payloadFields below for its own additive fields. Spain SII needs-correction ladder: invoice.accepted_with_errors fires exactly once per record when AEAT registers it with an admissible error that needs an A1 amendment (alongside the compliance email), and invoice.amend_due fires at each later stage of that ladder (due soon, deadline passed, recurring overdue). invoice.report_due_soon and invoice.report_overdue are the near-deadline and past-deadline reminders for an immediate-mode mandate transaction that has not yet been reported. ap.decision fires for a purchase (accounts-payable) tax calculation whose decision outcome is HELD, OVERCHARGED or UNDERCHARGED, or ACCEPTED_AS_CHARGED with a warning (a clean in-tolerance acceptance does not notify); its payload carries event, calculationId, entityId, supplierRef, outcome, reasonCode, legalDelta, appliedDelta, warning and recordedAt. filing.* events (filing.enabled, filing.ready, filing.approved, filing.paused, filing.resumed, filing.pause_ending, filing.payment_due, filing.submitted, filing.filed, filing.skipped, filing.not_filed, filing.action_needed) fire for US sales tax returns; the payload carries `event`, `eventId`, `filingId`, `jurisdictionId`, `periodKey`, `filingStatus`, `dueDate` and `amountDue`. Subscribe to the named events or `*`; sandbox filings fire to sandbox endpoints only."
            },
            "example": [
              "invoice.accepted",
              "invoice.rejected"
            ],
            "default": [
              "*"
            ]
          },
          "entityId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "The entity this webhook is pinned to (an entity-scoped key, or an entity-restricted organisation-scoped key); null for an account-wide webhook delivered for every entity in the organisation."
          },
          "active": {
            "type": "boolean",
            "description": "Returned by GET /webhooks (which lists only active webhooks)."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "x-payloadFields": {
          "description": "Every delivered payload carries event, submissionId, country, referenceId, clearanceStatus, terminal, updatedAt and entityId, plus event-specific extras. Extras added for the Spain SII / unified-mandate surface are listed here; a key is present only when it has a value.",
          "invoice.status_changed": {
            "description": "ONE general event fired on every status transition of an e-invoice record: RECEIVED at ingest, a hold, a release, a retry, an authority answer. It is a lean payload with a pointer (GET /v1/invoices/{id}) instead of the rich fields (rejectionDetail, documents) that the specific events keep. Signing, secret and x-taxually-* headers are the same as every other event. `*` does not include it; list it by name and choose statuses with `statuses`.",
            "event": "'invoice.status_changed'",
            "eventId": "string — stable id of this event, the same on every delivery retry: dedupe on it.",
            "seq": "integer — increases with every status change of one invoice. Deliveries are retried and can arrive out of order: order an invoice's events by seq and ignore one lower than the highest you have applied.",
            "id": "string — the record id (same value as submissionId).",
            "submissionId": "string — same value as id, kept for consistency with the other events.",
            "referenceId": "string | null",
            "country": "string",
            "documentType": "string",
            "clearanceStatus": "ClearanceStatus — equals `to`.",
            "from": "ClearanceStatus | null — the previous status; null for the first status of a record.",
            "to": "ClearanceStatus — the new status.",
            "statusClass": "StatusClass — class of `to`.",
            "terminal": "boolean — true when no further status change will occur.",
            "reason": "{ code: string | null, message: string | null, actor: 'authority' | 'rule' | 'system' | 'merchant' | 'platform', fixUrl?: string } — why it changed and who caused it.",
            "nextStep": "NextStep | null — { actor: 'merchant' | 'platform' | 'authority' | 'system', message: string, fixUrl?: string }; null when nothing is outstanding.",
            "at": "string — ISO 8601 timestamp of the change.",
            "updatedAt": "string — same value as at.",
            "entityId": "string | null",
            "organisationId": "string — present on tenant/partner and organisation hooks.",
            "test": "true — present only on a test event (POST /v1/webhooks/test)."
          },
          "mx (invoice.received for a Mexico CFDI record)": {
            "mx": "object — additive block, present only on country=MX records. Same shape as invoice_data.mx on the stored record: { tipoDeComprobante, usoCfdi, regimenFiscalEmisor, regimenFiscalReceptor, metodoPago, formaPago, lugarExpedicion, complements[] }. Carta Porte / Complemento de Recepción de Pagos complements pass through in complements[] but their specific fields are not individually extracted (Phase 1 scope).",
            "sourceChannel": "'MX_SAT_INBOUND' | 'MX_CLIENT_PUSH_INBOUND' — which ingestion path produced this record.",
            "flags": "array of { code, message } — code one of MX_RECEIVER_REGIME_MISMATCH, MX_RECEIVER_CP_MISMATCH, MX_EFOS_LISTED. Advisory only; never blocks the event from firing."
          },
          "br (invoice.summary_received / invoice.received / invoice.cancelled for a Brazil NF-e record)": {
            "br": "object — additive block, present only on country=BR records. stage ('summary' | 'full') distinguishes which firing produced this payload for a consumer subscribed to '*'. Carries: chave (44-digit access key), cSitNFe/cSitNFeLabel (resNFe-stage only), tpNF/finNFe/serie/nNF (full-procNFe-stage only), nProt/dhRecbto (SEFAZ's own protocol/receipt timestamp, present on both stages), dhEmi (issuer-declared emission date — display only, never the deadline anchor), structured issuer/dest objects (cnpj, ie, uf, name — never a bare CNPJ string), totals.total, acknowledgeBy/respondBy (this record's own Ciência/conclusive-response deadlines as UTC instants) plus acknowledgeByDate/respondByDate/respondByLastSafeDate (the SAME deadlines as Brazilian-local YYYY-MM-DD calendar dates — use these for display, never derive a date from the UTC instants), and flags (array of BrFlag)."
          },
          "invoice.business_status_changed": {
            "businessStatus": "string — the record's new native business_status value (regime-specific: FR/DE's nine-state vocabulary, or Brazil's CIENCIA/CONFIRMACAO/OPERACAO_NAO_REALIZADA/DESCONHECIMENTO/PRESUMIDA).",
            "canonicalBusinessStatus": "'RECEIVED' | 'ACKNOWLEDGED' | 'ACCEPTED' | 'DISPUTED' — the shared, regime-agnostic bucket every registered regime's native value maps onto.",
            "previousBusinessStatus": "string | null — the native business_status value immediately before this write.",
            "businessStatusSource": "'customer' | 'partner_sync' | 'deemed' | 'system' — who/what caused the change: a direct PATCH, a partner/network sync (France's accredited third-party platform), the day-90 deemed-acceptance presumption on statutory silence (Brazil only), or Clearvo's own automatic action (e.g. auto-Ciência).",
            "businessStatusActor": "string — free-form audit text for who/what performed the write.",
            "businessStatusLabel": "string — the regime-authored human label for the new native value (e.g. \"Acknowledged\", \"Confirmed\", \"Not recognised\").",
            "nProt": "string | null — Brazil only: SEFAZ's own protocol number, present only when the write came from a real signed manifestation (absent for a deemed write, and for every non-BR regime)."
          },
          "held_unmapped_decision": {
            "holdReason": "string — plain-language reason the transaction is held (why no rule matched).",
            "actionOwner": "'platform' | 'customer' — who must act. Always 'platform' for this event: a configuration gap, never the merchant's fault."
          },
          "de (invoice.received for a DE inbound document)": {
            "validationOutcome": "'E_INVOICE' | 'E_INVOICE_WITH_FLAGS' | 'FORMAT_ERROR' | 'PROFILE_NOT_SUFFICIENT' | 'NOT_AN_EINVOICE' | 'BUSINESS_RULE_ERROR' | 'NOT_VALIDATED' — the BMF three-tier bad-file taxonomy outcome (BUSINESS_RULE_ERROR: the file is well formed but fails business rules; NOT_VALIDATED: the checks could not run). E_INVOICE(_WITH_FLAGS) is a legally-sufficient e-invoice; every other value still fired this event (a real document landed either way, clearanceStatus RECEIVED_NEEDS_REVIEW) but flags it for manual review. Absent for a non-classified invoice.received event (Poland KSeF, France); Peppol AS4 receipts are classified and checked like the German intake channels and carry the same fields.",
            "intakeChannel": "string — which intake adapter delivered the document: PEPPOL_AS4 | DE_INBOUND_API | DE_INBOUND_DASHBOARD | DE_INBOUND_BULK | DE_INBOUND_EMAIL.",
            "receivedAt": "string (ISO 8601) — when the platform actually received the document, distinct from updatedAt.",
            "transactionDirection": "'sale' | 'purchase' — 'sale' for a self-billed document (the receiving entity is the seller) or one the entity itself issued, 'purchase' otherwise.",
            "validation": "object | null — what was checked and found: status, rule set and artefact version, error and warning counts, and the findings (see InboundValidation). States what was checked; never says a document is valid or compliant.",
            "attachments": "array — the supplier's own supporting documents (EN 16931 BG-24), listed only, never the bytes (see InboundAttachment).",
            "rendition": "'original' | 'generated' | 'none' — how GET /v1/documents/{id}?format=pdf answers: the archived PDF, a labelled reader copy rendered from the structured data (any received Peppol invoice), or not available.",
            "supersedes": "string | null — the earlier needs-review record this corrected re-send replaced."
          },
          "reporting_batch.* (ready_for_review / deadline_approaching / deadline_passed / overdue_reminder)": {
            "submissionId": "string — the reporting batch's UUID (the `id` on GET /v1/reporting-batches/{id}), not an invoice id.",
            "clearanceStatus": "'READY_FOR_REVIEW' | 'T_MINUS_2' | 'T_MINUS_1' | 'PASSED' | 'OVERDUE' — the reminder stage, upper-cased.",
            "terminal": "boolean — always false.",
            "referenceId": "null.",
            "version": "1 — payload schema version for the batch events.",
            "eventId": "string — UUID unique to this delivery; use it to dedupe.",
            "batchId": "string — the human-readable batch key (the `batchId` on GET /v1/reporting-batches).",
            "book": "'issued' | 'received' | null — the SII ledger book. Omitted on the deadline_passed fired by the batch_auto submitter's give-up path.",
            "recordCount": "integer — records in the batch at the time of the event. Omitted on the submitter's give-up deadline_passed.",
            "reportBy": "date — the batch's own report-by deadline (its earliest record's AEAT report-by date).",
            "daysOverdue": "integer — Spanish business days past reportBy: 0 for ready_for_review/deadline_approaching, 1 for deadline_passed, 2+ for overdue_reminder."
          },
          "spainSiiTerminal (invoice.accepted / accepted_with_errors / invoice.rejected for an ES_SII record)": {
            "aeatCsv": "string | null — AEAT Código Seguro de Verificación; null on Incorrecto.",
            "aeatEstado": "'Correcto' | 'AceptadoConErrores' | 'Incorrecto'.",
            "errorCode": "string | null — top-level AEAT error code (Incorrecto) or the admissible-error code (AceptadoConErrores).",
            "errorDescription": "string | null — AEAT's error text / plain-English summary for the code.",
            "needsCorrection": "boolean — true only for AceptadoConErrores: AEAT registered the invoice but an A1 amendment is still needed before the record is clean.",
            "reportBy": "date | null — the statutory AEAT report-by deadline (same value as reportBy on POST /send and reportByDate on GET /v1/mandate-transactions).",
            "retryable": "boolean — whether re-sending the identical registro could change the outcome. Always false: every terminal AEAT answer is a definitive per-invoice decision.",
            "merchantResolvable": "boolean — whether the merchant can fix this by correcting invoice data (Fix & Resubmit / A1 amend)."
          },
          "invoice.amended / invoice.cancelled (Spain SII block 3)": {
            "aeatCsv": "string | null — AEAT Código Seguro de Verificación for the new A1/Baja communication.",
            "operation": "'A1' | 'BAJA' — which AEAT communication this event reports on.",
            "communicationId": "string — the AEAT communication ledger row id for this specific attempt."
          },
          "invoice.reported / invoice.report_rejected (Portugal PT_EFATURA invoice-data communication)": {
            "submissionId": "string — the invoice record's id (the same `id` POST /send returned); fetch GET /v1/invoices/{id} for ptReportingDetail (AT's response code/message, communicatedAt, attempts).",
            "country": "'PT'.",
            "referenceId": "null — the document's own ATCUD is on the record, not repeated here.",
            "clearanceStatus": "'ACCEPTED' — the DOCUMENT's status, unchanged by this event even for invoice.report_rejected; the communication outcome is the event name itself.",
            "terminal": "boolean — always true (the document was already terminal at issuance)."
          }
        }
      },
      "TaxNumberValidateRequest": {
        "type": "object",
        "required": [
          "taxNumber"
        ],
        "properties": {
          "countryCode": {
            "type": "string",
            "minLength": 2,
            "maxLength": 2,
            "example": "PL",
            "description": "ISO 3166-1 alpha-2 country code. Optional: when omitted it is derived from a recognizable 2-letter ISO prefix on taxNumber (e.g. ESB81254336 resolves to ES); if neither is present the request is rejected with 400."
          },
          "taxNumber": {
            "type": "string",
            "example": "PL7740001454"
          },
          "registryType": {
            "type": "string",
            "enum": [
              "vat",
              "national",
              "eori",
              "auto"
            ],
            "default": "auto",
            "description": "auto tries VAT first, then national. eori: validates EORI number format; live authority verification is limited pending an upstream service migration."
          },
          "checkAuthority": {
            "type": "boolean",
            "default": true,
            "description": "Perform a real-time authority check (VIES, HMRC, Brreg, ABN, etc.)."
          },
          "force": {
            "type": "boolean",
            "default": false,
            "description": "Bypass the cache and always call the authority."
          },
          "bankAccountNumber": {
            "type": "string",
            "description": "IBAN to verify against the Polish VAT Whitelist (Biala Lista). Only meaningful for PL VAT numbers. When provided, both VIES and the Biala Lista are checked and authority.extra.whitelist.ibanRegistered indicates whether the account is registered for that taxpayer. Cache is bypassed when this field is set."
          },
          "localRegistryOnly": {
            "type": "boolean",
            "description": "Batch endpoints only (validate-batch, validate-batch/async); ignored by POST /tax-numbers/validate. For a country with a local registry (currently PL, BE) skips the primary VIES call and queries only the local registry. Ignored for countries without a local registry."
          }
        }
      },
      "TaxNumberValidateResponse": {
        "type": "object",
        "properties": {
          "countryCode": {
            "type": "string"
          },
          "taxNumber": {
            "type": "string"
          },
          "normalized": {
            "type": "string"
          },
          "registryType": {
            "type": "string",
            "enum": [
              "vat",
              "national",
              "eori"
            ],
            "description": "The registry the number was resolved against (never 'auto')."
          },
          "valid": {
            "type": "boolean",
            "description": "Overall validity: false when format is invalid or the authority check explicitly returned invalid; true otherwise (including when the authority check was skipped or unavailable)."
          },
          "formatValid": {
            "type": "boolean",
            "description": "Same value as format.valid (single-validate response only)."
          },
          "format": {
            "type": "object",
            "required": [
              "valid"
            ],
            "properties": {
              "valid": {
                "type": "boolean"
              },
              "normalized": {
                "type": "string"
              },
              "error": {
                "type": "string"
              }
            }
          },
          "authority": {
            "type": "object",
            "description": "Authority check outcome. Never null: when the authority check was skipped or unavailable this is {\"checked\": false}.",
            "required": [
              "checked"
            ],
            "properties": {
              "checked": {
                "type": "boolean"
              },
              "valid": {
                "type": "boolean",
                "description": "Present only when the authority returned a verdict."
              },
              "name": {
                "type": "string"
              },
              "address": {
                "type": "string"
              },
              "status": {
                "type": "string",
                "description": "Authority-reported registration status."
              },
              "entityType": {
                "type": "string"
              },
              "requestId": {
                "type": "string",
                "description": "Authority-issued consultation reference (e.g. VIES requestIdentifier). Stored permanently for audit."
              },
              "extra": {
                "type": "object",
                "additionalProperties": true,
                "description": "Country-specific enrichment. For PL VAT: extra.whitelist contains {requestId, status, registeredAccounts, checkDate, ibanRegistered}."
              },
              "error": {
                "type": "string",
                "description": "Present when the authority check failed or was unavailable."
              },
              "checkedAt": {
                "type": "string",
                "format": "date-time"
              },
              "gstRegistered": {
                "type": "boolean",
                "description": "AU only: whether the entity is GST-registered per the ABR (distinct from ABN validity)."
              }
            }
          },
          "alternatives": {
            "type": "array",
            "description": "Other interpretations of the input (e.g. a different country or registry) that also matched.",
            "items": {
              "type": "object",
              "properties": {
                "taxNumber": {
                  "type": "string"
                },
                "countryCode": {
                  "type": "string"
                },
                "reason": {
                  "type": "string"
                },
                "format": {
                  "type": "object",
                  "required": [
                    "valid"
                  ],
                  "properties": {
                    "valid": {
                      "type": "boolean"
                    },
                    "normalized": {
                      "type": "string"
                    },
                    "error": {
                      "type": "string"
                    }
                  }
                },
                "authority": {
                  "type": "object",
                  "description": "Authority check outcome. Never null: when the authority check was skipped or unavailable this is {\"checked\": false}.",
                  "required": [
                    "checked"
                  ],
                  "properties": {
                    "checked": {
                      "type": "boolean"
                    },
                    "valid": {
                      "type": "boolean",
                      "description": "Present only when the authority returned a verdict."
                    },
                    "name": {
                      "type": "string"
                    },
                    "address": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "description": "Authority-reported registration status."
                    },
                    "entityType": {
                      "type": "string"
                    },
                    "requestId": {
                      "type": "string",
                      "description": "Authority-issued consultation reference (e.g. VIES requestIdentifier). Stored permanently for audit."
                    },
                    "extra": {
                      "type": "object",
                      "additionalProperties": true,
                      "description": "Country-specific enrichment. For PL VAT: extra.whitelist contains {requestId, status, registeredAccounts, checkDate, ibanRegistered}."
                    },
                    "error": {
                      "type": "string",
                      "description": "Present when the authority check failed or was unavailable."
                    },
                    "checkedAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "gstRegistered": {
                      "type": "boolean",
                      "description": "AU only: whether the entity is GST-registered per the ABR (distinct from ABN validity)."
                    }
                  }
                },
                "cached": {
                  "type": "boolean"
                }
              }
            }
          },
          "cached": {
            "type": "boolean"
          },
          "sandbox": {
            "type": "boolean"
          },
          "checkedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "RegistrationRequest": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "STANDARD",
              "IOSS",
              "UNION_OSS",
              "NON_UNION_OSS",
              "SIMPLIFIED"
            ],
            "default": "STANDARD",
            "description": "STANDARD=per-country (VAT, GST, sales tax); IOSS=EU Import One-Stop Shop; UNION_OSS=EU OSS for EU-established sellers; NON_UNION_OSS=EU OSS for non-EU sellers; SIMPLIFIED=simplified digital-goods scheme (e.g. Norway VOEC — see `schemeLabel` on the list response for the country-aware display name). `VOEC` is no longer accepted as a `type` value; use `SIMPLIFIED`."
          },
          "country": {
            "type": "string",
            "minLength": 2,
            "maxLength": 2,
            "description": "ISO 3166-1 alpha-2 country code. Not required for IOSS (applies EU-wide).",
            "example": "US"
          },
          "region": {
            "type": "string",
            "nullable": true,
            "description": "Sub-national region code (e.g. a US state) for jurisdictions that register at region level. Omit or null for country-level registrations.",
            "example": "CA"
          },
          "locality": {
            "type": "string",
            "nullable": true,
            "description": "Locality code for US home-rule jurisdictions that require their own registration distinct from the state. See GET /tax/registrations/field-definitions for valid codes.",
            "example": "DENVER"
          },
          "taxNumber": {
            "type": "string",
            "description": "The registration number issued by the authority. Optional — omit to record the registration without a number yet; the obligation row is still created.",
            "example": "DE123456789"
          },
          "iossNumber": {
            "type": "string",
            "description": "IOSS number (used when type is IOSS).",
            "example": "IM1234567890"
          },
          "extraFields": {
            "type": "object",
            "nullable": true,
            "additionalProperties": {
              "type": "string"
            },
            "description": "Country-specific profile fields (e.g. IT regimeFiscale, FR siret, DE steuernummer), validated against GET /tax/registrations/field-definitions. For a US STANDARD registration in TX or AL, also carries `flatRateElection` — the canonical encoding of an opt-in election into that state's statutory flat-rate remote-seller program (TX's Single Local Use Tax Rate, Tex. Tax Code §151.0595; AL's Simplified Sellers Use Tax, Ala. Code §40-23-192). Clearvo only records an election the seller attests it has already filed with the state (see the attestation copy in GET /tax/registrations/field-definitions's `helpText`) — it does not file anything itself, nor verify eligibility. `setBy`/`setAt` inside the encoding are always re-stamped server-side; any value sent for them is ignored. Rejected with 400 INVALID_FLAT_RATE_ELECTION for any other country/region/scheme, an inactive program, or (TX only) a marketplace-facilitator account.",
            "example": {
              "flatRateElection": "{\"v\":1,\"programCode\":\"TX_SLUTR\",\"electedFrom\":\"2026-01-01\",\"electedTo\":null,\"setBy\":\"ignored\",\"setAt\":\"ignored\"}"
            }
          },
          "obligationStatus": {
            "type": "string",
            "enum": [
              "COMPLIANT",
              "MONITORING"
            ],
            "default": "COMPLIANT"
          },
          "collectFromDate": {
            "type": "string",
            "nullable": true,
            "description": "Optional — sets when tax collection starts in this same call, instead of a separate PATCH .../collection follow-up. Omit to leave collection unset (collectionStatus=SETUP_NEEDED); send null to start collecting immediately; an ISO date (YYYY-MM-DD) defers collection to that date.",
            "example": "2026-01-01"
          }
        }
      },
      "RegistrationItem": {
        "type": "object",
        "properties": {
          "country": {
            "type": "string",
            "example": "US"
          },
          "region": {
            "type": "string",
            "nullable": true,
            "description": "Sub-national region code (e.g. a US state), when this registration is region-scoped."
          },
          "locality": {
            "type": "string",
            "nullable": true,
            "description": "US home-rule locality code, when this registration is locality-scoped."
          },
          "scheme": {
            "type": "string",
            "enum": [
              "STANDARD",
              "OSS_UNION",
              "OSS_NON_UNION",
              "SIMPLIFIED",
              "IOSS"
            ],
            "description": "Internal scheme. STANDARD covers all per-country registrations (VAT, GST, sales tax). SIMPLIFIED replaces the old VOEC scheme value — see `schemeLabel` for the country-aware display name."
          },
          "schemeLabel": {
            "type": "string",
            "description": "Country-aware display label for `scheme` (e.g. renders 'VOEC' instead of 'SIMPLIFIED' for country=NO).",
            "example": "VOEC"
          },
          "taxNumber": {
            "type": "string",
            "nullable": true,
            "example": "1234567"
          },
          "taxType": {
            "type": "string",
            "nullable": true
          },
          "taxNumberId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "Set only when scheme=STANDARD. Use `obligationId` as the identifier for PATCH/DELETE against any scheme."
          },
          "registrationStatus": {
            "type": "string",
            "enum": [
              "REGISTERED",
              "PENDING",
              "MONITORING",
              "NOT_REGISTERED",
              "DEREGISTERED"
            ],
            "description": "DEREGISTERED means collection has ended for this registration (see collectUntilDate) — the row is kept for history rather than deleted."
          },
          "registrationStatusLabel": {
            "type": "string",
            "description": "Ready-to-render display label for `registrationStatus`.",
            "example": "Registered"
          },
          "obligationStatus": {
            "type": "string",
            "nullable": true,
            "description": "Null when Compliance Radar is switched off for the account (see `monitoring` on the list).",
            "enum": [
              "COMPLIANT",
              "APPROACHING",
              "BREACH",
              "MONITORING"
            ]
          },
          "obligationStatusLabel": {
            "type": "string",
            "nullable": true,
            "description": "Ready-to-render display label for `obligationStatus`. Null when obligationStatus itself is null, except \"Not monitored\" when Compliance Radar is switched off for the account.",
            "example": "Compliant"
          },
          "obligationId": {
            "type": "string",
            "nullable": true
          },
          "threshold": {
            "type": "object",
            "nullable": true,
            "properties": {
              "amount": {
                "type": "number"
              },
              "currency": {
                "type": "string"
              }
            }
          },
          "currentPeriodAmount": {
            "type": "number",
            "nullable": true
          },
          "estimatedExposure": {
            "type": "number",
            "nullable": true
          },
          "collectionStatus": {
            "type": "string",
            "nullable": true,
            "enum": [
              "COLLECTING",
              "DEFERRED",
              "SETUP_NEEDED"
            ],
            "description": "Null when Tax Calculations is not enabled for this entity."
          },
          "collectionStatusLabel": {
            "type": "string",
            "nullable": true,
            "description": "Ready-to-render display label for `collectionStatus`. Null when collectionStatus itself is null, or when it is SETUP_NEEDED (that state renders as an action prompt, not a status label).",
            "example": "Collecting"
          },
          "collectFromDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "collectUntilDate": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Set when this registration has been deregistered (registrationStatus=DEREGISTERED) — the moment collection stopped or is scheduled to stop. Null for an active registration."
          },
          "canCollectTax": {
            "type": "boolean"
          },
          "flatRateElection": {
            "$ref": "#/components/schemas/FlatRateElection"
          },
          "flatRateElectionWarning": {
            "$ref": "#/components/schemas/FlatRateElectionWarning"
          },
          "secondaryIdentifiers": {
            "type": "array",
            "description": "Jurisdiction-specific secondary identifiers on file for this registration (e.g. France's SIRET, Germany's Steuernummer) — set at creation via `extraFields` or edited later via PATCH .../{id}.",
            "items": {
              "type": "object",
              "properties": {
                "key": {
                  "type": "string",
                  "example": "fr_siret"
                },
                "label": {
                  "type": "string",
                  "example": "SIRET"
                },
                "value": {
                  "type": "string",
                  "example": "12345678901234"
                }
              }
            }
          }
        }
      },
      "RegistrationResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "taxNumberId": {
            "type": "string",
            "format": "uuid",
            "description": "Set only when the created/updated registration has scheme=STANDARD."
          },
          "obligationId": {
            "type": "string"
          },
          "registration": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "type": {
                "type": "string"
              },
              "country": {
                "type": "string"
              },
              "region": {
                "type": "string",
                "nullable": true
              },
              "locality": {
                "type": "string",
                "nullable": true
              },
              "taxNumber": {
                "type": "string"
              },
              "obligationStatus": {
                "type": "string",
                "nullable": true,
                "description": "Null when Compliance Radar is switched off for the account; `obligationStatusLabel` is then \"Not monitored\"."
              },
              "obligationStatusLabel": {
                "type": "string",
                "description": "Present only when Compliance Radar is switched off for the account: \"Not monitored\"."
              },
              "scheme": {
                "type": "string"
              },
              "schemeLabel": {
                "type": "string",
                "description": "Country-aware display label for `scheme` (e.g. 'VOEC' for country=NO)."
              },
              "collectFromDate": {
                "type": "string",
                "format": "date-time",
                "nullable": true,
                "description": "Reflects the collectFromDate passed in the request (if any) — confirms whether collection was actually activated by this call, without a follow-up GET."
              },
              "collectionStatus": {
                "type": "string",
                "nullable": true,
                "enum": [
                  "COLLECTING",
                  "DEFERRED",
                  "SETUP_NEEDED"
                ],
                "description": "Null when Tax Calculations is not enabled for this entity."
              },
              "flatRateElection": {
                "$ref": "#/components/schemas/FlatRateElection"
              },
              "flatRateElectionWarning": {
                "$ref": "#/components/schemas/FlatRateElectionWarning"
              }
            }
          }
        }
      },
      "ClientTaxCode": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "entityId": {
            "type": "string",
            "format": "uuid"
          },
          "code": {
            "type": "string",
            "example": "A1",
            "description": "Your own ERP tax code."
          },
          "country": {
            "type": "string",
            "example": "DE"
          },
          "region": {
            "type": "string",
            "nullable": true,
            "description": "Sub-national scope — a structured state/province/emirate for US, Canada, India, or UAE; free text elsewhere. Null for a country-wide code."
          },
          "movement": {
            "type": "string",
            "enum": [
              "local",
              "intra_community",
              "export",
              "distance_sale",
              "import",
              "own_goods_movement"
            ],
            "description": "The Tax Decision movement this code represents. `export` covers every non-EU-EU B2B cross-border combination — the EU/G vs. non-EU/AE split in the derived `taxCode` comes from whether `country` itself is in the EU, not from this field. Which values are valid depends on country and direction (e.g. country=US only ever accepts 'local') — see GET /tax/client-codes/options."
          },
          "taxability": {
            "type": "string",
            "enum": [
              "taxable",
              "exempt",
              "out_of_scope"
            ]
          },
          "customerType": {
            "type": "string",
            "nullable": true,
            "enum": [
              "b2b",
              "b2c"
            ],
            "description": "Null means this code applies to either b2b or b2c — the one place 'any' is a valid value; a resolved Tax Decision for a real transaction (e.g. on POST /tax/calculate's reverse lookup) is always a definite b2b or b2c."
          },
          "supplyType": {
            "type": "string",
            "enum": [
              "goods",
              "digital_service",
              "general_service"
            ],
            "description": "'unsupported' is a real value on a resolved transaction's own supply type, but is never selectable here — there is no way to derive a tax outcome for it."
          },
          "rateBand": {
            "type": "string",
            "nullable": true,
            "enum": [
              "standard",
              "middle",
              "reduced",
              "super_reduced",
              "special",
              "zero"
            ],
            "description": "Only meaningful when taxability=taxable and the movement/reverseCharge combination doesn't already fix the EN16931 code — null for intra_community, export, or a domestic reverse charge (movement=local with reverseCharge=true). Always null for country=US: a client tax code only carries a state-level region, never a real ship-to address, so it can't reproduce the address-level rate US sales tax actually resolves to (state + county + city + special-district) — no rate band is offered or accepted for US."
          },
          "reverseCharge": {
            "type": "boolean",
            "description": "Independent of movement — e.g. some countries require domestic reverse charge for specific goods categories even on a wholly local sale."
          },
          "useTaxSelfAssessed": {
            "type": "boolean"
          },
          "filingTag": {
            "type": "string",
            "nullable": true,
            "enum": [
              "cash_accounting_settled",
              "cash_accounting_unsettled",
              "split_payment",
              "statement_of_intent",
              "withholding",
              "bad_debt_adjustment",
              "triangular_party_b",
              "triangular_party_c"
            ],
            "description": "Pure metadata for a future Taxsure integration — never consumed by any computation."
          },
          "direction": {
            "type": "string",
            "nullable": true,
            "enum": [
              "sale",
              "purchase"
            ],
            "description": "Optional scope. Null (default) means the code applies to both."
          },
          "recoverabilityType": {
            "type": "string",
            "nullable": true,
            "enum": [
              "full",
              "blocked",
              "restricted"
            ],
            "description": "Input-tax recoverability — the entity's OWN position on a purchase-side code (can it reclaim the tax it was charged). Only applicable when direction='purchase' — rejected outright (VALIDATION_ERROR) if set on a direction='sale' code. 'full' and 'blocked' fully determine recoverablePercentage (100 / 0 respectively — any caller-supplied percentage is ignored and normalized). 'restricted' is the only case where recoverablePercentage is genuine data."
          },
          "recoverablePercentage": {
            "type": "number",
            "nullable": true,
            "description": "0–100 (a direct percentage, NOT a 0–1 fraction — unlike `rate`). Null unless recoverabilityType is set. Required, and must be strictly between 0 and 100, when recoverabilityType='restricted' (0 and 100 are already 'blocked'/'full')."
          },
          "taxCode": {
            "type": "string",
            "enum": [
              "S",
              "AA",
              "AB",
              "AC",
              "AD",
              "AE",
              "K",
              "G",
              "E",
              "O",
              "Z"
            ],
            "description": "EN16931 tax category code. Response-only, computed live from the fields above (see deriveEn16931Code's own rules) — never a stored or caller-supplied value."
          },
          "rate": {
            "type": "number",
            "nullable": true,
            "example": 0.19,
            "description": "Response-only, computed live from country + the derived taxCode (and, for a US row, the region's own state sales tax rate) — never a stored or caller-supplied value. Decimal fraction (0.19 = 19%) — matches the tax calculation engine's convention, not POST /send's own taxRate percentage field. Null when the rate can't currently be determined (e.g. a US row with no region, or a jurisdiction outside rate-table coverage)."
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "invoiceReferenceText": {
            "type": "string",
            "nullable": true,
            "description": "Free-text exemption wording, only meaningful for an exempt, out-of-scope, or reverse-charge code — passed through verbatim onto every invoice using this code, printed exactly as given. Never derived, translated, or auto-generated by the platform."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "exemptionReasonCode": {
            "type": "string",
            "nullable": true,
            "maxLength": 30,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*$",
            "description": "Optional reason code for a taxability=exempt/out_of_scope code (e.g. an exemption-scheme code). Pure metadata — never consumed by any tax computation."
          },
          "invoiceReferenceApplicable": {
            "type": "boolean",
            "description": "Response-only, backend-computed: true when invoiceReferenceText is meaningful for this code's movement/taxability/reverseCharge shape (an exempt or out_of_scope code, a reverse-charge code, or an intra_community/export movement). A value sent in a request is ignored."
          }
        }
      },
      "ClientTaxCodeRequest": {
        "type": "object",
        "required": [
          "code",
          "country",
          "movement",
          "taxability",
          "supplyType"
        ],
        "properties": {
          "code": {
            "type": "string",
            "maxLength": 50,
            "example": "A1"
          },
          "country": {
            "type": "string",
            "minLength": 2,
            "maxLength": 3,
            "example": "DE"
          },
          "region": {
            "type": "string",
            "nullable": true,
            "maxLength": 10
          },
          "movement": {
            "type": "string",
            "enum": [
              "local",
              "intra_community",
              "export",
              "distance_sale",
              "import",
              "own_goods_movement"
            ]
          },
          "taxability": {
            "type": "string",
            "enum": [
              "taxable",
              "exempt",
              "out_of_scope"
            ]
          },
          "customerType": {
            "type": "string",
            "nullable": true,
            "enum": [
              "b2b",
              "b2c"
            ],
            "description": "Omit or send null for a code that applies to either b2b or b2c."
          },
          "supplyType": {
            "type": "string",
            "enum": [
              "goods",
              "digital_service",
              "general_service"
            ]
          },
          "rateBand": {
            "type": "string",
            "nullable": true,
            "enum": [
              "standard",
              "middle",
              "reduced",
              "super_reduced",
              "special",
              "zero"
            ],
            "description": "Required when taxability=taxable and this movement/reverseCharge combination doesn't already fix the EN16931 code (e.g. required for movement=local without reverseCharge). Not applicable — and ignored if sent — for intra_community, export, or a domestic reverse charge (movement=local with reverseCharge=true). Never required, and always ignored/stored as null, for country=US regardless of taxability/movement — see GET /tax/client-codes/options, which returns rateBands: null for US."
          },
          "reverseCharge": {
            "type": "boolean",
            "description": "Defaults to false. Independent of movement."
          },
          "useTaxSelfAssessed": {
            "type": "boolean",
            "description": "Defaults to false."
          },
          "filingTag": {
            "type": "string",
            "nullable": true,
            "enum": [
              "cash_accounting_settled",
              "cash_accounting_unsettled",
              "split_payment",
              "statement_of_intent",
              "withholding",
              "bad_debt_adjustment",
              "triangular_party_b",
              "triangular_party_c"
            ],
            "description": "Pure metadata for a future Taxsure integration — never consumed by any computation."
          },
          "direction": {
            "type": "string",
            "nullable": true,
            "enum": [
              "sale",
              "purchase"
            ],
            "description": "Optional. Omit or send null for a code that applies to both."
          },
          "recoverabilityType": {
            "type": "string",
            "nullable": true,
            "enum": [
              "full",
              "blocked",
              "restricted"
            ],
            "description": "Only applicable when direction='purchase' — 400 VALIDATION_ERROR if set (or if recoverablePercentage is set) on a direction='sale' code. See ClientTaxCode.recoverabilityType for the full full/blocked/restricted contract."
          },
          "recoverablePercentage": {
            "type": "number",
            "nullable": true,
            "description": "0–100. Required and strictly between 0–100 when recoverabilityType='restricted'; ignored/normalized to 100 or 0 for 'full'/'blocked' respectively."
          },
          "description": {
            "type": "string",
            "nullable": true,
            "maxLength": 500
          },
          "invoiceReferenceText": {
            "type": "string",
            "nullable": true,
            "maxLength": 500,
            "description": "Free-text exemption wording, only meaningful for an exempt, out-of-scope, or reverse-charge code — passed through verbatim onto every invoice using this code. Never derived, translated, or auto-generated by the platform."
          },
          "exemptionReasonCode": {
            "type": "string",
            "nullable": true,
            "maxLength": 30,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*$",
            "description": "Optional reason code for a taxability=exempt/out_of_scope code (e.g. an exemption-scheme code). Pure metadata — never consumed by any tax computation."
          }
        }
      },
      "ClientTaxCodeUpdateRequest": {
        "type": "object",
        "description": "Any subset of ClientTaxCodeRequest's fields — omitted fields keep their current value.",
        "properties": {
          "code": {
            "type": "string",
            "maxLength": 50
          },
          "country": {
            "type": "string",
            "minLength": 2,
            "maxLength": 3
          },
          "region": {
            "type": "string",
            "nullable": true,
            "maxLength": 10
          },
          "movement": {
            "type": "string",
            "enum": [
              "local",
              "intra_community",
              "export",
              "distance_sale",
              "import",
              "own_goods_movement"
            ]
          },
          "taxability": {
            "type": "string",
            "enum": [
              "taxable",
              "exempt",
              "out_of_scope"
            ]
          },
          "customerType": {
            "type": "string",
            "nullable": true,
            "enum": [
              "b2b",
              "b2c"
            ]
          },
          "supplyType": {
            "type": "string",
            "enum": [
              "goods",
              "digital_service",
              "general_service"
            ]
          },
          "rateBand": {
            "type": "string",
            "nullable": true,
            "enum": [
              "standard",
              "middle",
              "reduced",
              "super_reduced",
              "special",
              "zero"
            ]
          },
          "reverseCharge": {
            "type": "boolean"
          },
          "useTaxSelfAssessed": {
            "type": "boolean"
          },
          "filingTag": {
            "type": "string",
            "nullable": true,
            "enum": [
              "cash_accounting_settled",
              "cash_accounting_unsettled",
              "split_payment",
              "statement_of_intent",
              "withholding",
              "bad_debt_adjustment",
              "triangular_party_b",
              "triangular_party_c"
            ]
          },
          "direction": {
            "type": "string",
            "nullable": true,
            "enum": [
              "sale",
              "purchase"
            ]
          },
          "recoverabilityType": {
            "type": "string",
            "nullable": true,
            "enum": [
              "full",
              "blocked",
              "restricted"
            ]
          },
          "recoverablePercentage": {
            "type": "number",
            "nullable": true
          },
          "description": {
            "type": "string",
            "nullable": true,
            "maxLength": 500
          },
          "invoiceReferenceText": {
            "type": "string",
            "nullable": true,
            "maxLength": 500,
            "description": "Free-text exemption wording — see ClientTaxCodeRequest. Send null to clear it."
          },
          "exemptionReasonCode": {
            "type": "string",
            "nullable": true,
            "maxLength": 30,
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*$",
            "description": "Optional reason code for a taxability=exempt/out_of_scope code (e.g. an exemption-scheme code). Pure metadata — never consumed by any tax computation."
          }
        }
      },
      "ClientTaxCodeOptions": {
        "type": "object",
        "description": "The single source of truth for 'given this country/region/direction, what's valid' when building or filtering a Client Tax Code form — the dashboard, MCP, CLI, and SDK all call GET /tax/client-codes/options rather than hardcoding their own copy of these rules, and the same rules are enforced server-side on POST/PATCH regardless of which client submits the request.",
        "properties": {
          "countries": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Countries Clearvo actually supports for a client tax code — the union of Tax Calculations' and E-Invoicing's own supported-jurisdiction lists."
          },
          "movements": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "value": {
                  "type": "string",
                  "enum": [
                    "local",
                    "intra_community",
                    "export",
                    "distance_sale",
                    "import",
                    "own_goods_movement"
                  ]
                },
                "label": {
                  "type": "string"
                },
                "valid": {
                  "type": "boolean"
                }
              }
            },
            "description": "Every movement value with a `valid` flag for the given country + direction — not pre-filtered, so a UI can grey out invalid options instead of hiding them. Unfiltered (every value `valid: true`) when no `country` query param is supplied."
          },
          "supplyTypes": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "value": {
                  "type": "string",
                  "enum": [
                    "goods",
                    "digital_service",
                    "general_service"
                  ]
                },
                "label": {
                  "type": "string"
                }
              }
            }
          },
          "rateBands": {
            "type": "array",
            "nullable": true,
            "items": {
              "type": "object",
              "properties": {
                "value": {
                  "type": "string",
                  "enum": [
                    "standard",
                    "middle",
                    "reduced",
                    "super_reduced",
                    "special",
                    "zero"
                  ]
                },
                "label": {
                  "type": "string"
                },
                "valid": {
                  "type": "boolean"
                },
                "rate": {
                  "type": "number",
                  "nullable": true,
                  "description": "Live decimal-fraction percentage (0.19 = 19%) for this country/region, e.g. for labelling an option 'Standard rate (19%)'. Null when not valid or not currently resolvable."
                }
              }
            },
            "description": "Null when no `country` query param is supplied — there is nothing to compute yet. Also null for country=US: a client tax code only carries a state-level region, never a real ship-to address, so no rate band can be meaningfully offered (see ClientTaxCode's own rateBand field description)."
          },
          "regionRequired": {
            "type": "boolean",
            "nullable": true,
            "description": "True for the countries with a structured region picker (US, Canada, India, UAE). Null when no `country` supplied."
          },
          "regionOptions": {
            "type": "array",
            "nullable": true,
            "items": {
              "type": "object",
              "properties": {
                "value": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                }
              }
            },
            "description": "Selectable sub-country regions when the country offers a fixed list (ES returns the Canary Islands region), as value/label pairs to send as `region`. Null when the country has no fixed list or no `country` supplied."
          },
          "regionNoneLabel": {
            "type": "string",
            "nullable": true,
            "description": "Label for the empty choice when `regionOptions` is offered (ES: mainland). Null otherwise."
          },
          "reverseChargeRelevant": {
            "type": "boolean",
            "nullable": true,
            "description": "Whether the reverseCharge toggle is a meaningful concept for this country (true for every VAT/GST country, false for the US). Null when no `country` supplied."
          },
          "useTaxSelfAssessedRelevant": {
            "type": "boolean",
            "nullable": true,
            "description": "Whether useTaxSelfAssessed is a meaningful concept for this country (true only for the US). Null when no `country` supplied."
          },
          "zeroRatedGuidance": {
            "type": "string",
            "nullable": true,
            "description": "Present only when 'zero' is a valid rate band for this country — guidance that a zero-rated transaction should be configured as taxability=taxable with rateBand=zero, not taxability=exempt."
          }
        }
      },
      "DuplicateTreatmentWarning": {
        "type": "object",
        "nullable": true,
        "description": "Present only when another client tax code for this entity already maps to the identical (country, region, movement, direction, customerType, supplyType, reverseCharge, useTaxSelfAssessed) treatment — rateBand is deliberately excluded from this key, since it's redundant once the rest of the tuple is fixed. Non-blocking — the write still succeeds.",
        "properties": {
          "code": {
            "type": "string",
            "enum": [
              "DUPLICATE_TREATMENT"
            ]
          },
          "message": {
            "type": "string"
          },
          "conflictingCodeId": {
            "type": "string",
            "format": "uuid"
          },
          "conflictingCode": {
            "type": "string"
          }
        }
      },
      "FlatRateElection": {
        "type": "object",
        "nullable": true,
        "description": "A US remote-seller flat-rate election on file for this registration (TX Single Local Use Tax Rate, Tex. Tax Code §151.0595; or AL Simplified Sellers Use Tax, Ala. Code §40-23-192). Null when no election is set. Clearvo only records the election — it does not file it with the state or verify eligibility.",
        "properties": {
          "programCode": {
            "type": "string",
            "example": "TX_SLUTR"
          },
          "electedFrom": {
            "type": "string",
            "format": "date",
            "example": "2026-01-01"
          },
          "electedTo": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "setBy": {
            "type": "string",
            "description": "Clerk user id (or account id, for an API-key-only actor) that set/last changed this election. Always re-stamped server-side — never trusted from the request."
          },
          "setAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "FlatRateElectionWarning": {
        "type": "object",
        "nullable": true,
        "description": "Non-blocking eligibility signal returned alongside a present flatRateElection — the election is persisted regardless; this never blocks the write.",
        "properties": {
          "code": {
            "type": "string",
            "enum": [
              "POSSIBLE_PHYSICAL_PRESENCE"
            ],
            "description": "Fires when the entity's own on-file business address is in the same state as the election — both programs are remote-seller-only."
          },
          "message": {
            "type": "string"
          }
        }
      },
      "Certificate": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "ids": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "All certificate IDs grouped under this reference (one per region)."
          },
          "certificateRef": {
            "type": "string",
            "example": "EXEMPT-2024-001"
          },
          "customerRef": {
            "type": "string",
            "nullable": true,
            "description": "Your internal customer reference, used to look up the certificate during tax calculation.",
            "example": "CUST-001"
          },
          "customerName": {
            "type": "string",
            "nullable": true,
            "example": "Acme Corp"
          },
          "customerTaxId": {
            "type": "string",
            "nullable": true,
            "example": "US123456789"
          },
          "certificateType": {
            "type": "string",
            "description": "Valid values depend on `country` — see POST /tax/exemptions for the full per-country list.",
            "enum": [
              "RESALE",
              "MANUFACTURING",
              "AGRICULTURAL",
              "ENERGY",
              "EXEMPT_ORG",
              "GOVERNMENT",
              "DIRECT_PAY",
              "BLANKET_OTHER",
              "DIGITAL_SERVICES_COMMERCIAL_USE",
              "EXPORT_AUTHORIZATION"
            ]
          },
          "formType": {
            "type": "string",
            "nullable": true,
            "enum": [
              "SST",
              "MTC",
              "CUSTOM",
              "31-014A",
              "56B"
            ]
          },
          "country": {
            "type": "string",
            "minLength": 2,
            "maxLength": 2,
            "example": "US"
          },
          "regions": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "State/region codes covered by this certificate.",
            "example": [
              "CA",
              "NY"
            ]
          },
          "regionNames": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Full names of the regions covered.",
            "example": [
              "California",
              "New York"
            ]
          },
          "taxCategorySlug": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "ACTIVE",
              "EXPIRING_SOON",
              "EXPIRED",
              "REVOKED"
            ],
            "description": "EXPIRING_SOON and EXPIRED are set automatically by a daily renewal job as effectiveTo approaches/passes; REVOKED is the only caller-settable transition via PATCH."
          },
          "effectiveFrom": {
            "type": "string",
            "format": "date",
            "example": "2024-01-01"
          },
          "effectiveTo": {
            "type": "string",
            "format": "date",
            "nullable": true,
            "example": "2024-12-31"
          },
          "documentUrl": {
            "type": "string",
            "format": "uri",
            "nullable": true
          },
          "expiryStatus": {
            "type": "string",
            "enum": [
              "ACTIVE",
              "EXPIRING_SOON",
              "EXPIRED"
            ],
            "description": "Freshly computed from effectiveTo on every request — trust this over `status` if they ever disagree, since `status` is only refreshed by the daily renewal job."
          },
          "needsDocument": {
            "type": "boolean",
            "description": "True when no document has been uploaded and the certificate is not revoked."
          },
          "statusTier": {
            "type": "string",
            "enum": [
              "expiring_soon",
              "expired",
              "revoked",
              "needs_document",
              "active"
            ],
            "description": "Ready-to-use classification combining status, expiryStatus, and needsDocument with a fixed precedence — use this to drive UI colour/urgency instead of re-deriving it from the three raw fields."
          },
          "statusLabel": {
            "type": "string",
            "description": "Ready-to-render display label for statusTier.",
            "example": "Expiring Soon"
          },
          "numberFormatStatus": {
            "type": "string",
            "nullable": true,
            "enum": [
              "OK",
              "MISMATCH",
              "NO_RULE",
              null
            ],
            "description": "Result of the warn-only check of `certificateRef` against the state's documented certificate number format. OK: matches. MISMATCH: does not match (the certificate was still saved; see numberFormatWarning). NO_RULE: no format is held for this state or certificate type, so nothing was checked. Null: not checked (for example a certificate created before the check existed)."
          },
          "numberFormatWarning": {
            "type": "string",
            "nullable": true,
            "description": "Plain-English message to show the user when numberFormatStatus is MISMATCH. Null otherwise.",
            "example": "The certificate number does not match the usual Texas format (11 digits). The certificate was saved; check the number against the buyer's permit."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Customer": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Clearvo's internal customer ID."
          },
          "customerRef": {
            "type": "string",
            "nullable": true,
            "description": "Your own reference for this customer (e.g. a CRM/ERP customer id). Pass as customer.customerRef on POST /send to reuse a saved customer without resending full customer details.",
            "example": "CUST-001"
          },
          "name": {
            "type": "string",
            "example": "Acme Retail Ltd"
          },
          "nameNormalized": {
            "type": "string",
            "description": "Lowercased, whitespace-collapsed form of name, used internally for exact-match lookups."
          },
          "country": {
            "type": "string",
            "nullable": true,
            "minLength": 2,
            "maxLength": 2,
            "description": "ISO 3166-1 alpha-2 country code of the PRIMARY tax registration (taxIds[0]). Required together with taxId; a B2C customer may have neither.",
            "example": "IE"
          },
          "taxId": {
            "type": "string",
            "nullable": true,
            "description": "Tax ID of the primary registration, format-validated and normalized against country. See taxIds for a customer registered in more than one country.",
            "example": "IE1234567T"
          },
          "taxIds": {
            "type": "array",
            "description": "Every tax registration on file for this customer, primary first (mirrors country/taxId above). A customer registered in more than one country has more than one entry here.",
            "items": {
              "type": "object",
              "properties": {
                "country": {
                  "type": "string",
                  "minLength": 2,
                  "maxLength": 2,
                  "example": "IE"
                },
                "taxId": {
                  "type": "string",
                  "example": "IE1234567T"
                }
              }
            }
          },
          "addressCountry": {
            "type": "string",
            "nullable": true,
            "minLength": 2,
            "maxLength": 2,
            "description": "ISO 3166-1 alpha-2 country code of this customer's own mailing address — independent of country (the primary tax registration's country above). A customer can be VAT-registered in one country and addressed in another. Defaults to the primary tax registration's country when not given explicitly.",
            "example": "IE"
          },
          "addressLine1": {
            "type": "string",
            "nullable": true
          },
          "addressLine2": {
            "type": "string",
            "nullable": true
          },
          "city": {
            "type": "string",
            "nullable": true
          },
          "region": {
            "type": "string",
            "nullable": true
          },
          "postalCode": {
            "type": "string",
            "nullable": true
          },
          "source": {
            "type": "string",
            "enum": [
              "manual",
              "API",
              "bulk_import",
              "auto_captured"
            ],
            "description": "How this record was created: entered manually on the dashboard, created via this API, bulk/CSV-imported, or auto-captured from an issued invoice's customer details."
          },
          "references": {
            "type": "array",
            "description": "This customer's typed references: identifiers that are not tax numbers, such as a Peppol participant ID or a German Leitweg-ID. Replaces the former top-level `peppol` object. The available types, their validation and the countries they apply to are listed by GET /v1/customer-reference-types. Tax and VAT numbers are not references; they stay in country/taxId/taxIds.",
            "items": {
              "$ref": "#/components/schemas/CustomerReference"
            }
          },
          "countrySpecific": {
            "type": "object",
            "description": "Country-specific master data for this customer, only ever meaningful for the named country — harmless (simply unused) for any other.",
            "properties": {
              "it": {
                "type": "object",
                "description": "Italy SDI routing. Backfilled onto countrySpecific.it.codiceDestinatario/pecDestinatario on POST /send (via customer.customerRef) when the request doesn't supply them directly. Unlike a Peppol participant ID reference, there is no live network to verify these against, so there is no confirmed-at timestamp — whatever is stored is used as-is.",
                "properties": {
                  "codiceDestinatario": {
                    "type": "string",
                    "nullable": true,
                    "minLength": 7,
                    "maxLength": 7,
                    "description": "This customer's own 7-character SDI routing channel code. Omitting it on POST /send defaults to '0000000' (B2C) or 'XXXXXXX' (non-Italian customer) — both legally valid, but SDI only actually delivers the invoice to the customer when a real channel code is present.",
                    "example": "ABC1234"
                  },
                  "pecDestinatario": {
                    "type": "string",
                    "format": "email",
                    "nullable": true,
                    "description": "This customer's PEC (certified email) delivery address, used alongside or instead of codiceDestinatario for an Italian B2C customer."
                  }
                }
              },
              "de": {
                "type": "object",
                "description": "DE ZUGFeRD/XRechnung format override for this customer.",
                "properties": {
                  "invoiceFormat": {
                    "type": "string",
                    "nullable": true,
                    "enum": [
                      "ZUGFERD",
                      "XRECHNUNG",
                      "PEPPOL"
                    ],
                    "description": "Applied by POST /send whenever the request omits countrySpecific.de.invoiceFormat for this customer. Null (the default) means no per-customer override is set — the entity/platform default applies instead."
                  }
                }
              },
              "ar": {
                "type": "object",
                "description": "Argentina AFIP/ARCA buyer VAT condition for this customer.",
                "properties": {
                  "condicionIVAReceptorId": {
                    "type": "integer",
                    "nullable": true,
                    "enum": [
                      1,
                      4,
                      5,
                      6,
                      7,
                      8,
                      9,
                      10,
                      13,
                      15,
                      16
                    ],
                    "description": "Backfilled onto countrySpecific.ar.condicionIVAReceptorId on POST /send (via customer.customerRef) whenever the request doesn't supply its own value for this AR invoice. ARCA's real FEParamGetCondicionIvaReceptor code (RG (ARCA) 5616/2024): 1=Responsable Inscripto, 4=IVA Sujeto Exento, 5=Consumidor Final, 6=Responsable Monotributo, 7=Sujeto No Categorizado, 8=Proveedor del Exterior, 9=Cliente del Exterior, 10=IVA Liberado (Ley 19.640), 13=Monotributista Social, 15=IVA No Alcanzado, 16=Monotributo Trabajador Independiente Promovido. The obsolete codes 2 (Responsable no Inscripto) and 3 (No Responsable) are no longer valid. Null (the default) means no stored value — the request must then supply its own condicionIVAReceptorId or the AR submission defaults to 5 (Consumidor Final)."
                  }
                }
              }
            }
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "email": {
            "type": "string",
            "nullable": true,
            "description": "Contact email used to default invoice notifications (and, for a supplier, self-billing notifications) when the request supplies none. Read-only on this API: returned in responses, not settable via the public endpoints."
          }
        }
      },
      "CustomerReferenceType": {
        "type": "object",
        "description": "One kind of reference a customer can carry beyond a tax number, from the platform-wide registry returned by GET /v1/customer-reference-types.",
        "required": [
          "code",
          "label",
          "description",
          "helpText",
          "example",
          "countries",
          "mandates",
          "allowMultiple",
          "requiresConfirmation",
          "easSchemeCode",
          "validation"
        ],
        "properties": {
          "code": {
            "type": "string",
            "description": "Stable identifier; use it as `type` when writing a customer's `references`.",
            "example": "LEITWEG_ID"
          },
          "label": {
            "type": "string",
            "example": "Leitweg-ID"
          },
          "description": {
            "type": "string"
          },
          "helpText": {
            "type": "string",
            "nullable": true,
            "description": "Guidance suitable for showing next to an input."
          },
          "example": {
            "type": "string",
            "nullable": true,
            "example": "04011000-1234512345-06"
          },
          "countries": {
            "type": "array",
            "nullable": true,
            "items": {
              "type": "string"
            },
            "description": "ISO 3166-1 alpha-2 codes of the customer countries this type applies to; null means any country.",
            "example": [
              "DE"
            ]
          },
          "mandates": {
            "type": "array",
            "nullable": true,
            "items": {
              "type": "string"
            },
            "description": "Compliance mandate codes this type applies to; null means any mandate."
          },
          "allowMultiple": {
            "type": "boolean",
            "description": "False means at most one reference of this type per customer (a second one is DUPLICATE_REFERENCE_TYPE)."
          },
          "requiresConfirmation": {
            "type": "boolean",
            "description": "True means a derived or imported value must be confirmed by a person before it is used for live routing (the Peppol participant ID). A value you supply through the API is treated as confirmed."
          },
          "easSchemeCode": {
            "type": "string",
            "nullable": true,
            "description": "The Peppol EAS scheme code for a plain value of this type (0204 for a Leitweg-ID). Null when the value carries its own scheme as `scheme:value`.",
            "example": "0204"
          },
          "validation": {
            "type": "object",
            "required": [
              "kind",
              "pattern"
            ],
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "NONE",
                  "PATTERN",
                  "VALIDATOR"
                ],
                "description": "NONE: any value. PATTERN: a regular expression. VALIDATOR: a built-in check (for example the Leitweg-ID check digits)."
              },
              "pattern": {
                "type": "string",
                "nullable": true,
                "description": "Regular expression, present only when kind is PATTERN."
              }
            }
          }
        }
      },
      "CustomerReferenceInput": {
        "type": "object",
        "required": [
          "type",
          "value"
        ],
        "properties": {
          "type": {
            "type": "string",
            "description": "A `code` from GET /v1/customer-reference-types (case-insensitive).",
            "example": "LEITWEG_ID"
          },
          "value": {
            "type": "string",
            "example": "04011000-1234512345-06"
          }
        }
      },
      "CustomerReference": {
        "type": "object",
        "description": "One stored reference on a customer. `reachable` and `checkedAt` appear only for a type that requires confirmation (PEPPOL_PARTICIPANT_ID).",
        "required": [
          "type",
          "value",
          "confirmed"
        ],
        "properties": {
          "type": {
            "type": "string",
            "example": "PEPPOL_PARTICIPANT_ID"
          },
          "value": {
            "type": "string",
            "description": "For PEPPOL_PARTICIPANT_ID the form is `scheme:value`, for example 0204:04011000-1234512345-06 or 9930:DE123456789.",
            "example": "0204:04011000-1234512345-06"
          },
          "confirmed": {
            "type": "boolean",
            "description": "Always true for a type that does not require confirmation. For a Peppol participant ID, only a confirmed value is used by POST /send."
          },
          "reachable": {
            "type": "boolean",
            "nullable": true,
            "description": "Whether the Peppol participant ID was last found on the Peppol network. Informational only; never used to gate sending."
          },
          "checkedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When `reachable` was last checked."
          }
        }
      },
      "CustomerReferencesInvalidResponse": {
        "type": "object",
        "description": "422 returned by POST /customers, PATCH /customers/{id} and PUT /customers/by-ref/{customerRef} when `references` fails validation. Nothing was written.",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message",
              "references"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "INVALID_REFERENCES"
                ]
              },
              "message": {
                "type": "string"
              },
              "references": {
                "type": "array",
                "description": "Every failing item, not just the first.",
                "items": {
                  "type": "object",
                  "required": [
                    "code",
                    "index",
                    "type",
                    "message"
                  ],
                  "properties": {
                    "code": {
                      "type": "string",
                      "enum": [
                        "INVALID_REFERENCE",
                        "UNKNOWN_REFERENCE_TYPE",
                        "REFERENCE_TYPE_NOT_APPLICABLE",
                        "DUPLICATE_REFERENCE_TYPE"
                      ],
                      "description": "INVALID_REFERENCE: the value failed the type's validation. UNKNOWN_REFERENCE_TYPE: no such type. REFERENCE_TYPE_NOT_APPLICABLE: the type does not apply to this customer's country. DUPLICATE_REFERENCE_TYPE: a second item of a type that allows only one."
                    },
                    "index": {
                      "type": "integer",
                      "description": "Position of the failing item in the submitted `references` list."
                    },
                    "type": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "Supplier": {
        "type": "object",
        "description": "Supplier master-data record — mirrors Customer for the other side of a transaction. A received inbound e-invoice is linked to a supplier master row (matched by entity/country/tax ID), and `supplier.ref` on a `transactionDirection: 'purchase'` tax calculation resolves this record, filling name/taxId/address. `customer.ref` on a tax calculation is unrelated — it never resolves stored Customer data, only auto-applies ECM exemption certificates.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Clearvo's internal supplier ID."
          },
          "supplierRef": {
            "type": "string",
            "nullable": true,
            "description": "Your own reference for this supplier (e.g. an ERP vendor id). Pass as supplier.ref on POST /tax/calculate (transactionDirection: 'purchase') to reuse a saved supplier without resending full details.",
            "example": "VEND-001"
          },
          "name": {
            "type": "string",
            "example": "Acme Supplies Ltd"
          },
          "country": {
            "type": "string",
            "nullable": true,
            "minLength": 2,
            "maxLength": 2,
            "description": "ISO 3166-1 alpha-2 country code of the PRIMARY tax registration (taxIds[0]). Required together with taxId; a supplier with no known VAT registration may have neither.",
            "example": "IE"
          },
          "taxId": {
            "type": "string",
            "nullable": true,
            "description": "Tax ID of the primary registration, format-validated and normalized against country. See taxIds for a supplier registered in more than one country.",
            "example": "IE1234567T"
          },
          "taxIds": {
            "type": "array",
            "description": "Every tax registration on file for this supplier, primary first (mirrors country/taxId above). A supplier registered in more than one country has more than one entry here.",
            "items": {
              "type": "object",
              "properties": {
                "country": {
                  "type": "string",
                  "minLength": 2,
                  "maxLength": 2,
                  "example": "IE"
                },
                "taxId": {
                  "type": "string",
                  "example": "IE1234567T"
                }
              }
            }
          },
          "establishmentCountry": {
            "type": "string",
            "nullable": true,
            "minLength": 2,
            "maxLength": 2,
            "description": "ISO 3166-1 alpha-2 country code of this supplier's own place of establishment — independent of country (the primary tax registration's country above). Defaults to the primary tax registration's country when not given explicitly.",
            "example": "IE"
          },
          "addressLine1": {
            "type": "string",
            "nullable": true
          },
          "addressLine2": {
            "type": "string",
            "nullable": true
          },
          "city": {
            "type": "string",
            "nullable": true
          },
          "region": {
            "type": "string",
            "nullable": true
          },
          "postalCode": {
            "type": "string",
            "nullable": true
          },
          "source": {
            "type": "string",
            "enum": [
              "manual",
              "API",
              "bulk_import",
              "auto_captured"
            ],
            "description": "How this record was created: entered manually on the dashboard, created via this API, bulk/CSV-imported, or auto-captured from a received inbound e-invoice's supplier details."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "trusted": {
            "type": "boolean",
            "description": "AP decision layer full override (lib/ap/decision.ts) — when true, every vendor-charged-tax verification for this supplier resolves ACCEPTED_AS_CHARGED / TRUSTED_SUPPLIER_OVERRIDE regardless of delta. Enterprise only (Purchases (AP) sub-solution): setting it without Purchases (AP) is refused with 403 `ap_not_included` or `ap_not_enabled`; clearing is always allowed."
          },
          "toleranceOverride": {
            "type": "object",
            "nullable": true,
            "description": "Per-supplier tolerance override for vendor-charged-tax verification — takes precedence over the entity's own default apTolerance (see GET/PATCH /tax/settings). null means no override. Enterprise only (Purchases (AP) sub-solution): setting it without Purchases (AP) is refused with 403 `ap_not_included` or `ap_not_enabled`; clearing is always allowed.",
            "properties": {
              "absoluteAmount": {
                "type": "number",
                "minimum": 0
              },
              "percent": {
                "type": "number",
                "minimum": 0
              }
            }
          },
          "email": {
            "type": "string",
            "nullable": true,
            "description": "Contact email used to default invoice notifications (and, for a supplier, self-billing notifications) when the request supplies none. Read-only on this API: returned in responses, not settable via the public endpoints."
          }
        }
      },
      "BankAccount": {
        "type": "object",
        "description": "Bank-account master data (IBAN/BIC) so you never have to resend your own bank details on every POST /send call. One account per currency, plus an optional entity-wide DEFAULT account (currency omitted/null) used when no currency-specific account matches an invoice. On a send, an explicit payment.means[].iban you supply always wins outright — this master data is only ever a fallback: (1) your own payment.means[].iban on that call; (2) your stored account for the invoice's currency; (3) your stored DEFAULT account; (4) nothing.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Clearvo's internal bank account ID."
          },
          "currency": {
            "type": "string",
            "nullable": true,
            "minLength": 3,
            "maxLength": 3,
            "description": "ISO 4217 alpha-3 currency code, or null for the entity-wide DEFAULT account.",
            "example": "EUR"
          },
          "iban": {
            "type": "string",
            "description": "Format/checksum-validated IBAN.",
            "example": "IE29AIBK93115212345678"
          },
          "bic": {
            "type": "string",
            "nullable": true,
            "description": "BIC/SWIFT code.",
            "example": "AIBKIE2D"
          },
          "accountHolderName": {
            "type": "string",
            "nullable": true,
            "description": "Dashboard display only — never rendered onto an invoice."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "InboundReceiveResult": {
        "type": "object",
        "description": "The POST /receive (and per-file POST /receive/bulk outcome) response shape.",
        "required": [
          "ok",
          "id",
          "clearanceStatus",
          "validationOutcome",
          "documentFormat"
        ],
        "properties": {
          "ok": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "id": {
            "type": "string"
          },
          "country": {
            "type": "string"
          },
          "clearanceStatus": {
            "type": "string",
            "enum": [
              "ACCEPTED",
              "RECEIVED_NEEDS_REVIEW"
            ],
            "description": "ACCEPTED when validationOutcome is E_INVOICE/E_INVOICE_WITH_FLAGS; RECEIVED_NEEDS_REVIEW for every other outcome — the document still lands, just flagged."
          },
          "terminal": {
            "type": "boolean"
          },
          "validationOutcome": {
            "type": "string",
            "enum": [
              "E_INVOICE",
              "E_INVOICE_WITH_FLAGS",
              "FORMAT_ERROR",
              "PROFILE_NOT_SUFFICIENT",
              "NOT_AN_EINVOICE",
              "BUSINESS_RULE_ERROR",
              "NOT_VALIDATED"
            ],
            "description": "The BMF three-tier bad-file taxonomy outcome. Also carried on the invoice.received webhook payload — see the `de (invoice.received for a DE inbound document)` entry under WebhookEndpoint's own x-payloadFields."
          },
          "documentFormat": {
            "type": "string",
            "enum": [
              "PDF_ZUGFERD",
              "PDF_XRECHNUNG",
              "XML_CII",
              "XML_UBL",
              "PDF_UNSTRUCTURED",
              "UNKNOWN"
            ]
          },
          "errorCode": {
            "type": "string",
            "nullable": true
          },
          "suggestedAction": {
            "type": "string",
            "nullable": true
          },
          "duplicate": {
            "type": "boolean",
            "nullable": true,
            "description": "true when this exact document (byte-identical, or the same supplier/number/date) was already stored — id/duplicateOf point at the existing record; nothing was re-processed."
          },
          "duplicateOf": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "InboundBulkBatchStatus": {
        "type": "object",
        "description": "Shared with GET /send/bulk-async/{batchId} — bulk_upload_batches is the one tracker for both kinds.",
        "properties": {
          "batchId": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "UPLOADED",
              "VALIDATING",
              "COMPLETED",
              "FAILED",
              "CANCELLED"
            ]
          },
          "filename": {
            "type": "string",
            "nullable": true
          },
          "uploadedBy": {
            "type": "string",
            "nullable": true
          },
          "total": {
            "type": "integer"
          },
          "outcomeCounts": {
            "type": "object",
            "description": "Inbound-receive batches key this by ACCEPTED / NEEDS_REVIEW / DUPLICATE / ERRORED / COUNTRY_NOT_ENABLED — zero (key absent) for any outcome this batch had no files land in.",
            "additionalProperties": {
              "type": "integer"
            }
          },
          "summary": {
            "type": "string",
            "description": "Human-readable, always derived from outcomeCounts."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "validatedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "completedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "InboundEmailAddressStatus": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "country": {
            "type": "string",
            "enum": [
              "DE"
            ]
          },
          "address": {
            "type": "string",
            "nullable": true,
            "description": "'<token>@inbound.clearvo.io', or null if never issued."
          },
          "enabled": {
            "type": "boolean",
            "description": "True once an address has been issued."
          },
          "environment": {
            "type": "string",
            "enum": [
              "sandbox",
              "production"
            ]
          },
          "instructions": {
            "type": "string"
          }
        }
      },
      "RateLimitExceededError": {
        "type": "object",
        "description": "The 429 body returned when a per-key rate limit or the bulk rows budget is exceeded — see the Rate limits section of the API overview. `error.retryable` is always true: wait `Retry-After` seconds and retry.",
        "required": [
          "ok",
          "error"
        ],
        "properties": {
          "ok": {
            "type": "boolean",
            "enum": [
              false
            ]
          },
          "error": {
            "type": "object",
            "required": [
              "code",
              "message",
              "retryable"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "RATE_LIMIT_EXCEEDED"
                ]
              },
              "message": {
                "type": "string",
                "example": "Rate limit exceeded for this API key: 120 requests per minute on this endpoint class. Retry after 37 seconds."
              },
              "retryable": {
                "type": "boolean",
                "enum": [
                  true
                ]
              }
            }
          },
          "hint": {
            "type": "string",
            "example": "You've exceeded the rate limit for this API key. Spread requests out, honour Retry-After, or contact support@clearvo.io for higher limits."
          }
        }
      },
      "InvalidCountryError": {
        "type": "object",
        "description": "422 body for a country that is not a valid ISO 3166-1 alpha-2 code (for example XX, Narnia or UK). A valid country we do not support is not this error: it returns 200 with taxTreatment UNSUPPORTED_JURISDICTION.",
        "required": [
          "error",
          "code",
          "field",
          "value",
          "message"
        ],
        "properties": {
          "error": {
            "type": "string",
            "enum": [
              "invalid_country"
            ]
          },
          "code": {
            "type": "string",
            "enum": [
              "INVALID_COUNTRY"
            ]
          },
          "field": {
            "type": "string",
            "description": "Request path of the malformed country, for example customer.shippingAddress.country."
          },
          "value": {
            "type": "string",
            "description": "The value that was sent."
          },
          "message": {
            "type": "string"
          },
          "hint": {
            "type": "string"
          }
        }
      },
      "Error": {
        "type": "object",
        "description": "Standard error response shape, returned on every 4xx/5xx.",
        "properties": {
          "ok": {
            "type": "boolean",
            "example": false
          },
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "example": "VALIDATION_ERROR",
                "description": "Stable, machine-readable identifier — branch on this, never on `message`."
              },
              "message": {
                "type": "string",
                "example": "Invalid request: missing required field 'country'",
                "description": "Human-readable explanation."
              },
              "retryable": {
                "type": "boolean",
                "nullable": true,
                "description": "Present only when meaningfully true or false — e.g. true on a 429."
              }
            }
          },
          "hint": {
            "type": "string",
            "nullable": true,
            "description": "One plain-language sentence on what to do about it."
          },
          "code": {
            "type": "string",
            "nullable": true,
            "example": "VALIDATION_ERROR",
            "description": "Also present at the top level on some responses, alongside `error.code`."
          },
          "source": {
            "type": "string",
            "nullable": true,
            "enum": [
              "header",
              "line"
            ],
            "description": "Present only on POST /send's clientTaxCode resolution errors (UNKNOWN_CLIENT_TAX_CODE, DIRECTION_MISMATCH, CLIENT_TAX_CODE_RATE_UNAVAILABLE) — whether the offending code came from the invoice-level clientTaxCode or a specific line's own clientTaxCode."
          },
          "details": {
            "type": "object",
            "nullable": true,
            "additionalProperties": true
          }
        }
      },
      "TaxAddress": {
        "type": "object",
        "properties": {
          "line1": {
            "type": "string",
            "example": "123 Main St"
          },
          "line2": {
            "type": "string"
          },
          "city": {
            "type": "string",
            "example": "San Francisco"
          },
          "region": {
            "type": "string",
            "description": "State or region code. Required for US addresses (e.g. \"CA\", \"TX\").",
            "example": "CA"
          },
          "postalCode": {
            "type": "string",
            "example": "94105"
          },
          "country": {
            "type": "string",
            "minLength": 2,
            "maxLength": 3,
            "example": "US"
          }
        },
        "required": [
          "country"
        ]
      },
      "ClassificationCode": {
        "type": "object",
        "description": "One outside product code and the system it comes from. Clearvo looks the code up in its classification code map and, when it maps, uses the matching tax category. system 'stripe' = a Stripe product tax code (txcd_*), matched exactly. system 'shopify' = a Shopify product taxonomy category id (e.g. aa-1-13); a code with no entry of its own uses its nearest mapped parent category. system 'hs' = a customs commodity code; the longest mapped prefix wins. Other lowercase system ids are accepted; a code from a system Clearvo does not know, or one the map has no entry for, is simply skipped.",
        "required": [
          "system",
          "code"
        ],
        "properties": {
          "system": {
            "type": "string",
            "pattern": "^[a-z0-9_]{1,30}$",
            "description": "Lowercase identifier of the code system, 1-30 characters of a-z, 0-9 and underscore.",
            "example": "stripe"
          },
          "code": {
            "type": "string",
            "minLength": 1,
            "description": "The product code in that system, exactly as the system publishes it.",
            "example": "txcd_10701401"
          }
        }
      },
      "TaxTreatmentOverride": {
        "type": "string",
        "description": "Caller-forced rate band for a line item on POST /v1/tax/calculate — names a BAND only (matches the same closed set a resolved rate band can take), never a raw numeric rate; the engine still resolves the actual percentage for the line's own jurisdiction from that band. Optional on every line item. When supplied, this is the highest-precedence input to rate-band resolution — checked before any commodityCode lookup or database rate-band query — and flows straight into treatment determination exactly like a naturally-resolved band, replacing only the tail B2C-default rate-band step. Every upstream B2B/reverse-charge/exemption decision and the line's own tax category/classification resolution run unconditionally first and are completely untouched by this field. A band with no researched rate for the line's jurisdiction degrades the same way an unresolved band always does — this never silently invents a numeric rate. Exception: STANDARD, MIDDLE, REDUCED, SUPER_REDUCED, and SPECIAL have no meaning for a line that resolves to a US jurisdiction (US sales tax is a single combined state/county/city/district percentage per address, not a VAT-style banded rate table) and are rejected with a 422 us_tax_treatment_override_unsupported error rather than silently applying the ordinary calculated rate. ZERO and EXEMPT are rate-independent and work identically for every jurisdiction, including the US.",
        "enum": [
          "STANDARD",
          "MIDDLE",
          "REDUCED",
          "SUPER_REDUCED",
          "SPECIAL",
          "ZERO",
          "EXEMPT"
        ]
      },
      "TaxCalculateRequest": {
        "type": "object",
        "required": [
          "currency",
          "lineItems"
        ],
        "description": "`customer` and `supplier` are both optional, direction-aware party fields — the counterparty for the resolved `transactionDirection` is required, the entity side is optional (auto-enriched from your entity's own master data, or validated against it if you supply it) — see `transactionDirection`, and `entityRole` on the response.",
        "properties": {
          "companyCode": {
            "type": "string",
            "maxLength": 50,
            "description": "Optional company code (a division of the entity; Enterprise plans only). Trimmed, max 50, matched without regard to case against the entity's active codes (GET /company-codes). Omitted: the entity's default company code, else none. An unknown or archived code is HTTP 422 company_code_unknown before anything is calculated or stored; a code not valid on the transaction's own invoice/tax-point date (outside its validFrom/validTo window) is HTTP 422 company_code_not_valid_on_date; an entity default outside its window is simply not applied (companyCodeSource 'none' plus a warning); a plan without company codes is HTTP 403 company_codes_not_included. Company-code-scoped rules fire only for the resolved code. Also accepted by POST /tax/calculate/partner."
          },
          "idempotencyKey": {
            "type": "string",
            "description": "Optional idempotency key. When commit=true and the same key is replayed, the cached response is returned without re-calculating.",
            "example": "inv-2024-001"
          },
          "currency": {
            "type": "string",
            "minLength": 3,
            "maxLength": 3,
            "example": "USD"
          },
          "taxReportingCurrency": {
            "type": "string",
            "minLength": 3,
            "maxLength": 3,
            "description": "Optional secondary currency for reporting. Converted at current exchange rate.",
            "example": "EUR"
          },
          "date": {
            "type": "string",
            "description": "Transaction date (ISO 8601). Defaults to today.",
            "example": "2024-01-15"
          },
          "commit": {
            "type": "boolean",
            "default": true,
            "description": "Default: true (as of 2026-09-12). When the resolved value is true (sent explicitly, or omitted), the calculation is persisted to the tax_calculations table, appears in your dashboard/audit trail, counts toward your calculation usage, and contributes to Compliance Radar's nexus-threshold totals. Pass \"commit\": false explicitly for a preview/quote that should not be recorded, billed, or counted toward any threshold."
          },
          "vatUnverifiableFallback": {
            "type": "string",
            "enum": [
              "conservative",
              "permissive"
            ],
            "default": "conservative",
            "description": "How to treat a buyer whose VAT number cannot be verified. conservative=charge tax; permissive=treat as B2B."
          },
          "vatValidation": {
            "type": "string",
            "enum": [
              "full",
              "format",
              "none"
            ],
            "description": "Overrides the account's default VAT-ID validation mode for this call. full=live-authority check (e.g. VIES); format=structural check only, no live lookup; none=skip validation entirely. Omit to use the account's configured default."
          },
          "merchantRef": {
            "type": "string",
            "maxLength": 100,
            "description": "Your internal transaction reference, stored on the committed calculation.",
            "example": "order_abc123"
          },
          "orderDiscount": {
            "type": "number",
            "minimum": 0,
            "description": "An order-level discount (a code, automatic promotion, bundle, or loyalty credit) not tied to a single line — the complement of the per-line discount. Allocated across the lines proportionally to each line's post-line-discount net taxable base (rounded per-line in cents so the split reconciles exactly), reducing each line's taxable base on top of that line's own discount. An order discount larger than the total taxable base discounts it to zero and no further. Reflected in summary.totalDiscount.",
            "example": 15.0
          },
          "paymentMethod": {
            "type": "string",
            "enum": [
              "card",
              "bank_transfer",
              "digital_wallet",
              "cash",
              "other"
            ],
            "description": "Optional, informational only — never affects the calculation. Collected for reporting obligations that ask for it (e.g. Mexico's SAT real-time access rule)."
          },
          "isMarketplaceFacilitatedSale": {
            "type": "boolean",
            "description": "Set true when a marketplace facilitator (not this seller) is the party legally liable for collecting and remitting tax on this sale. Informational/audit-only — does not change the calculated rate or treatment."
          },
          "transactionType": {
            "type": "string",
            "enum": [
              "invoice",
              "credit_note"
            ],
            "default": "invoice"
          },
          "relatedCalculationId": {
            "type": "string",
            "description": "Required when transactionType is credit_note and commit is true. The ID of the original invoice being reversed.",
            "example": "cl_calc_a1b2c3d4e5f6g7h8i9j0"
          },
          "externalOriginalReference": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "Your own reference for a credit note's original invoice when Clearvo has no record of it — e.g. an invoice issued by a previous tax engine before you migrated onto Clearvo. Mutually exclusive with relatedCalculationId: exactly one of the two is required when transactionType is credit_note and commit is true (422 otherwise). `date` is REQUIRED on this path — Clearvo has nothing to reconstruct the original invoice's rate/date from, so the request is rejected (422) rather than priced at today's rate. Free text; not validated or resolved against anything — an audit-trail reference for your own records.",
            "example": "INV-2025-000418"
          },
          "transactionDirection": {
            "type": "string",
            "enum": [
              "sale",
              "purchase"
            ],
            "default": "sale",
            "description": "'sale' (default) — send `customer` (required); `supplier` optional, auto-filled from entity master data, validated if supplied (422 SUPPLIER_TAX_ID_MISMATCH). 'purchase' — send `supplier` (required, the vendor); `customer` optional/auto-filled the same way (422 CUSTOMER_TAX_ID_MISMATCH). `entityRole` in the response names your own block. `seller` is a deprecated sale-only `supplier` alias — 422 SELLER_ALIAS_NOT_ALLOWED_FOR_PURCHASE on a purchase. See the docs' Purchase Mode section for the full contract. Purchase direction is the Purchases (AP) sub-solution, available on the Enterprise plan and only when the business has Purchases (AP) switched on (Settings > Solutions): otherwise 403 `ap_not_included` (plan; `upgradeUrl` returned) or `ap_not_enabled` (switch it on; `hint` returned)."
          },
          "seller": {
            "type": "object",
            "deprecated": true,
            "description": "Deprecated alias for `supplier` on a `transactionDirection: 'sale'` calculation only — normalised to `supplier` before validation runs. Use `supplier` directly instead. Rejected with 422 SELLER_ALIAS_NOT_ALLOWED_FOR_PURCHASE when sent alongside `transactionDirection: 'purchase'` (there is no sensible mapping — use `supplier` there, which already means the vendor).",
            "properties": {
              "name": {
                "type": "string"
              },
              "taxId": {
                "type": "string"
              },
              "address": {
                "$ref": "#/components/schemas/TaxAddress"
              }
            }
          },
          "customer": {
            "type": "object",
            "description": "The customer party. Required when transactionDirection is 'sale' or omitted — the counterparty on a sale (422 CUSTOMER_REQUIRED if missing). Optional when transactionDirection is 'purchase' — there it is the entity side, auto-enriched from your entity's own master data when omitted; if supplied, its taxId must be one of the entity's registered tax IDs (any country — see POST /tax/registrations), else 422 CUSTOMER_TAX_ID_MISMATCH.",
            "properties": {
              "name": {
                "type": "string",
                "description": "Free-text display name. Optional and unused for tax determination."
              },
              "b2bOverride": {
                "type": "boolean",
                "description": "Force B2B treatment regardless of VAT verification result."
              },
              "taxId": {
                "type": "string",
                "description": "Customer VAT or tax ID. Used to determine B2B/B2C and verify reverse-charge eligibility."
              },
              "exemptionRef": {
                "type": "string",
                "description": "Reference to an exemption certificate stored in Clearvo ECM (the certificate_ref from POST /v1/tax/exemptions, not the customer's tax ID). When provided, the matching active certificate's exemption is applied to eligible line items."
              },
              "ref": {
                "type": "string",
                "description": "Your own reference for this customer (e.g. CRM ID, account number). Does NOT resolve any stored customer master data — it only auto-looks up active ECM exemption certificates for this customer and applies them to eligible US line items. Contrast `supplier.ref` on a purchase, which DOES fill name/taxId/billingAddress from the Suppliers master."
              },
              "billingAddress": {
                "$ref": "#/components/schemas/TaxAddress"
              },
              "shippingAddress": {
                "$ref": "#/components/schemas/TaxAddress"
              }
            }
          },
          "supplier": {
            "type": "object",
            "description": "The supplier party. Required on 'purchase' — the vendor on the purchase invoice (422 SUPPLIER_REQUIRED if missing); only `taxId`/`billingAddress.country` are read, as the vendor's VAT ID/country. `ref` resolves saved supplier master data (POST/GET /suppliers), filling taxId/address you omit — an unknown ref returns 422 SUPPLIER_REF_NOT_FOUND. Optional on 'sale' — there it's the entity side, auto-enriched from master data when omitted; a supplied taxId must be one of the entity's registered tax IDs (any country), else 422 SUPPLIER_TAX_ID_MISMATCH.",
            "properties": {
              "name": {
                "type": "string",
                "description": "Free-text display name. Accepted but unused for supplier resolution."
              },
              "b2bOverride": {
                "type": "boolean",
                "description": "Accepted but ignored — irrelevant to a supplier party in either direction."
              },
              "taxId": {
                "type": "string",
                "description": "Supplier VAT or tax ID — used to determine cross-border reverse-charge/import treatment on a purchase, or validated against the entity's own registration when supplier is the entity side on a sale."
              },
              "exemptionRef": {
                "type": "string",
                "description": "Accepted but ignored — exemption certificates apply to the customer side of a sale, not to a supplier party in either direction."
              },
              "ref": {
                "type": "string",
                "description": "Your own reference for this supplier (e.g. ERP vendor ID). On a purchase, resolves saved supplier master data — see POST/GET /suppliers — filling in any of name/taxId/address you omit here; a ref that does not resolve to a saved supplier of this entity returns 422 SUPPLIER_REF_NOT_FOUND (field 'supplier'). Ignored when supplier is the entity side (a sale)."
              },
              "billingAddress": {
                "$ref": "#/components/schemas/TaxAddress",
                "description": "The supplier's own country/address — the real dispatch/origin signal for cross-border purchase treatment."
              },
              "shippingAddress": {
                "$ref": "#/components/schemas/TaxAddress",
                "description": "Accepted but not meaningful for a purchase — ignored."
              }
            }
          },
          "shipFrom": {
            "$ref": "#/components/schemas/TaxShipFromAddress",
            "description": "Real dispatch/goods-origin address for a 'sale' — the order's fulfilment origin. Falls back to the seller's registered country when omitted (the common single-warehouse case); for a US dispatch with no `region`, the state (and ZIP code, if none sent) of the seller's own address is used. The address actually used is echoed on the response as `shipFrom`, `shipFromRegion` and `shipFromPostalCode`. Feeds the EU VAT Directive Art. 138(1)(b) dispatch-country check and B2C incoterms import-liability determination below, AND drives US origin sourcing for the origin-sourcing states (TX/CA/IL), overriding your account's place-of-business address; a per-line shipFrom overrides this again for that line. It is the dispatch country, and when omitted on a sale it defaults to your entity's own country of establishment; the response reports the country used as `shipFromCountry` with `shipFromSource`. For a sale of goods to a consumer in another EU country it also decides where VAT is charged (Art. 59c of Directive 2006/112/EC): dispatch from your own country of establishment, with no Union OSS or destination registration and recorded EU consumer sales at or under the EU distance-sales limit this year and last year, charges your home-country VAT; any other dispatch country charges VAT where the goods arrive."
          },
          "shipTo": {
            "$ref": "#/components/schemas/TaxAddress",
            "description": "Physical receiving address, same shape as shipFrom (country required; region required for US). Read on a 'purchase' only, and only when sent. A US address makes the line US consumer's use tax at that address (see lineItems[].useTax; a US state that is not enabled for use tax stays the 422 us_jurisdiction_purchase_unsupported); an address in your own country refines a goods line's destination (region, postal code); an address in any other country is not used and the response carries a ship_to_country_not_supported warning. With no shipTo a purchase resolves exactly as before, from your entity's own address. A line-level lineItems[].shipTo wins over this one. A sale ignores it."
          },
          "incoterms": {
            "type": "string",
            "enum": [
              "EXW",
              "FCA",
              "FAS",
              "FOB",
              "CFR",
              "CIF",
              "CPT",
              "CIP",
              "DAP",
              "DPU",
              "DDP"
            ],
            "description": "Optional Incoterms® 2020 rule for the shipment. Does not gate IOSS eligibility: IOSS applies whenever ship-from is non-EU, destination is EU, and value is ≤€150, regardless of incoterms. Above that threshold (or for any B2C cross-border physical-goods shipment with no applicable value-threshold scheme at all), this drives import-VAT liability: omitting it or supplying 'DDP' (seller is importer of record) leaves the ordinary registration-based treatment unchanged; any other value (EXW, FCA, FAS, FOB, CFR, CIF, CPT, CIP, DAP, DPU — buyer is importer of record) makes the line outside the scope of Clearvo tax calculation (taxCode 'O', rate 0), since the buyer's own customs process collects import VAT on that transaction, regardless of the seller's own registration status in the destination country. B2B is never affected — B2B cross-border goods already resolve to the correct reverse-charge/export code independent of incoterms. On a `transactionDirection: 'purchase'` calculation this is informational only for who imports (see `importerOfRecord`, which takes precedence when supplied) unless `importerOfRecord` is omitted, in which case incoterms maps to it (DDP → SELLER, every other rule → BUYER)."
          },
          "importerOfRecord": {
            "type": "string",
            "enum": [
              "SELLER",
              "BUYER"
            ],
            "description": "Explicit override for who is the importer of record on a cross-border, customs-crossing shipment — when supplied, takes precedence over whatever `incoterms` would otherwise imply. 'SELLER' behaves like incoterms 'DDP'; 'BUYER' behaves like any buyer-liable incoterm. On `transactionDirection: 'purchase'`, resolution is request value > incoterms > the entity's configured `defaultImporterOfRecord` (see TaxSettings); an otherwise-genuine cross-border GOODS purchase line with every tier empty is flagged `lineItems[].outcome: { status: 'UNKNOWN_TREATMENT', reasonCode: 'missing_importer_of_record' }` rather than guessed. On `transactionDirection: 'sale'` this remains informational metadata only (persisted/echoed, not read by treatment determination) — a sale's import-VAT liability is the customer's question, out of scope for what this engine computes on the sale side. Optional and additive."
          },
          "clientTaxCode": {
            "type": "string",
            "deprecated": true,
            "description": "Not accepted on this endpoint: a client tax code is a calculation OUTPUT here (see lineItems[].clientTaxCode on the response). Supplying it is rejected with 422 — send it to POST /v1/send instead."
          },
          "insurance": {
            "type": "object",
            "required": [
              "amount"
            ],
            "description": "Transaction-level insurance on the consignment, used as part of the customs value when duties are requested. Ignored unless duties are requested.",
            "properties": {
              "amount": {
                "type": "number",
                "minimum": 0,
                "example": 12.5
              },
              "currency": {
                "type": "string",
                "maxLength": 20,
                "description": "ISO 4217 currency of `amount`. Defaults to the transaction currency."
              }
            }
          },
          "shippingMode": {
            "type": "string",
            "enum": [
              "post",
              "courier"
            ],
            "description": "How the consignment travels. Read only when duties are requested: it selects the low-value regime (for example Canada: courier from the US or Mexico up to CAD 150 duty-free versus postal up to CAD 20). Optional and additive; also accepted on POST /tax/calculate/partner. Omitted means unknown: the stricter regime outcome is assumed and the consignment carries the estimate reason `shipping_mode_assumed` when the outcome would differ by mode."
          },
          "duties": {
            "type": "object",
            "required": [
              "include"
            ],
            "description": "Estimated import duty, import VAT/GST and customs fees for cross-border consignments. `include` overrides the account duties setting for this request; omit the whole object to follow the setting. Calculating duties never changes the tax figures, and a duties failure never fails or degrades the calculation. `collectDeposit: true` with `include: false` is rejected with 422.",
            "properties": {
              "include": {
                "type": "boolean",
                "description": "Calculate duties on this request."
              },
              "collectDeposit": {
                "type": "boolean",
                "description": "Collect the estimated import charges at checkout (a deposit) rather than leaving them payable on delivery."
              },
              "defaultCountryOfOrigin": {
                "type": "string",
                "pattern": "^[A-Za-z]{2}$",
                "description": "ISO 3166-1 alpha-2 origin used for lines that do not state their own `countryOfOrigin`."
              },
              "ratePolicyForHs6": {
                "type": "string",
                "enum": [
                  "modal",
                  "highest"
                ],
                "description": "How a 6-digit commodity code that spans several national tariff lines is resolved to one rate."
              }
            }
          },
          "lineItems": {
            "type": "array",
            "minItems": 1,
            "maxItems": 100,
            "items": {
              "type": "object",
              "required": [
                "id"
              ],
              "properties": {
                "id": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 200,
                  "description": "Your line item identifier. Returned in the response for matching.",
                  "example": "line_1"
                },
                "productName": {
                  "type": "string",
                  "maxLength": 200,
                  "example": "Software subscription"
                },
                "productDescription": {
                  "type": "string",
                  "maxLength": 500
                },
                "productCode": {
                  "type": "string",
                  "maxLength": 40,
                  "example": "SKU-001"
                },
                "taxCategory": {
                  "type": "string",
                  "maxLength": 100,
                  "description": "Clearvo tax category slug (e.g. software, saas, food_basic). Overrides catalogue-based classification.",
                  "example": "saas"
                },
                "classificationCodes": {
                  "type": "array",
                  "maxItems": 5,
                  "items": {
                    "$ref": "#/components/schemas/ClassificationCode"
                  },
                  "description": "Outside product codes for this line (Stripe tax code, Shopify taxonomy category, customs commodity code, ...), up to 5. Codes are tried in the order given and the first one that maps to a Clearvo category wins, ahead of the per-account product catalogue lookup. A code the map marks as ambiguous or as having no matching category is skipped, as is an unknown system or a code with no entry; if nothing maps, classification falls through to the product-name and account-default steps. The response reports which code decided in classificationSystem and classificationMatchedCode.",
                  "example": [
                    {
                      "system": "stripe",
                      "code": "txcd_10701401"
                    }
                  ]
                },
                "taxCode": {
                  "type": "string",
                  "maxLength": 100,
                  "description": "EN16931 tax code override (S, AA, AE, E, K, G, Z, O)."
                },
                "taxTreatmentOverride": {
                  "$ref": "#/components/schemas/TaxTreatmentOverride"
                },
                "shipFrom": {
                  "$ref": "#/components/schemas/TaxShipFromAddress",
                  "description": "Per-line fulfilment origin for a multi-warehouse order — overrides the transaction-level shipFrom for THIS line only. Sets this line's dispatch country for the EU consumer goods rule (Art. 59c) and, for the US origin-sourcing states (TX/CA/IL), where local tax sources to the seller's place of business: a line whose origin is a US address in the destination state origin-sources to that address; a line whose origin is elsewhere destination-sources. No effect on non-US lines or destination-sourced states. Supply only for a line shipping from a different location than the rest of the order. Effective origin precedence per line: this field > transaction shipFrom > your account's place-of-business address."
                },
                "commodityCode": {
                  "type": "string",
                  "maxLength": 40,
                  "description": "Optional tariff/customs classification code for this line item (e.g. an HS, CN, or UK Trade Tariff code). Three uses: (1) keys into Clearvo's internal tariff-rate data the same way taxCategory keys into category rate data, to set this line's rate band — that backing data is not itself a retrievable resource (no GET endpoint exposes it); (2) on an IOSS-treated calculation, drives the response's customsDuty object regardless of any override — lines sharing the same normalised classification count as one duty item. (3) when duties are requested, the code the line's import duty is priced at, with its scheme in `commodityCodeScheme` (inferred from the digit count and the destination when omitted; a national code is exact only in its own territory, anywhere else its first six digits are used). For uses (1) and (2) there is no separate scheme: HS, CN, and UK Trade Tariff codes all extend the same WCO Harmonized System numbering for their shared leading digits, and matching is jurisdiction-scoped by the line's own resolved country — never assumed shared across countries, e.g. GB vs. an EU member state — rather than by which nomenclature label a code happens to use. For use (1), looked up hierarchy-aware: the code's own digit precision is tried first, then progressively shorter standard prefixes (10 → 8 → 6 → 4 digits), each scoped to the line's own resolved country/region, taking the first level with a matching rate; consulted only when taxTreatmentOverride is absent. A total miss at every level never invents a rate — the line re-enters the ordinary taxCategory/classification cascade exactly as if this field had never been supplied.",
                  "example": "6109100010"
                },
                "commodityCodeScheme": {
                  "type": "string",
                  "enum": [
                    "HS6",
                    "CN8",
                    "TARIC10",
                    "UK10",
                    "HTS10"
                  ],
                  "description": "Scheme of this line's own `commodityCode`. Read when duties are requested, and readable by rules as the read-only property `commodityCodeScheme`."
                },
                "countryOfOrigin": {
                  "type": "string",
                  "pattern": "^[A-Za-z]{2}$",
                  "description": "ISO 3166-1 alpha-2 country of origin of the goods. Never inferred from `shipFrom`. Only read when duties are requested.",
                  "example": "CN"
                },
                "weight": {
                  "type": "object",
                  "required": [
                    "value",
                    "unit"
                  ],
                  "description": "Per-UNIT weight; the line weight is value x quantity. Only read when duties are requested.",
                  "properties": {
                    "value": {
                      "type": "number",
                      "minimum": 0
                    },
                    "unit": {
                      "type": "string",
                      "enum": [
                        "kg",
                        "g",
                        "lb",
                        "oz"
                      ]
                    }
                  }
                },
                "screenSizeInches": {
                  "type": "number",
                  "exclusiveMinimum": 0,
                  "maximum": 1000,
                  "description": "Diagonal screen size in inches of a TV, monitor, laptop, tablet or portable DVD player. Read only by the California Covered Electronic Device recycling fee, which is tiered by size (over 4 and under 15 inches $4, 15 to under 35 $5, 35 and over $6, per unit). Never affects tax. When omitted the lowest tier is applied and the fee entry in summary.retailDeliveryFees[] carries appliedDefault true."
                },
                "clientTaxCode": {
                  "type": "string",
                  "deprecated": true,
                  "description": "Not accepted on this endpoint: a calculation OUTPUT here. Rejected with 422 — send it to POST /v1/send instead."
                },
                "stripeTaxCode": {
                  "deprecated": true,
                  "description": "No longer accepted — send classificationCodes: [{ \"system\": \"stripe\", \"code\": \"txcd_...\" }] instead."
                },
                "amount": {
                  "type": "number",
                  "description": "Line total. Supply EITHER this, OR unitPrice together with quantity — never both forms, and never neither. Whether this is tax-inclusive or tax-exclusive is resolved by amountIncludesTax if set on this line, else the product's own configured pricing, else your account's defaultPriceIncludesTax setting (see TaxSettings) — which is exclusive by default for a US-resolved line regardless of your account setting, and inclusive by default for every other jurisdiction.",
                  "example": 100.0
                },
                "unitPrice": {
                  "type": "number",
                  "minimum": 0,
                  "description": "Per-unit price, the alternative to supplying amount directly. When supplied, quantity is REQUIRED and the line total is computed once as round2(unitPrice × quantity) and taxed as amount — so you do not pre-multiply and absorb per-unit rounding. Tax-inclusive vs -exclusive is resolved exactly as for amount. Mutually exclusive with amount; a unitPrice with no quantity is rejected (never silently assumed to be 1).",
                  "example": 9.99
                },
                "quantity": {
                  "type": "number",
                  "description": "Number of units. REQUIRED when unitPrice is supplied (the two together define the line total); otherwise informational only — when amount is supplied directly, quantity is not consumed by the engine.",
                  "example": 1
                },
                "amountIncludesTax": {
                  "type": "boolean",
                  "description": "Explicitly marks this line's amount as tax-inclusive (true) or tax-exclusive (false). Takes precedence over any product-level or account-level pricing default, for every jurisdiction including the US — e.g. a fixed-price digital subscription or a marketplace/platform absorbing tax. Omit to fall through to the account's defaultPriceIncludesTax setting."
                },
                "discount": {
                  "type": "number",
                  "description": "Decimal-unit amount subtracted directly from this line's amount to produce the taxable base (taxableAmount = amount − discount). NOT a percentage.",
                  "example": 10
                },
                "discountAmount": {
                  "type": "number",
                  "description": "Audit-trail/display metadata ONLY — not a tax input. Stored verbatim alongside the line item; never participates in taxableAmount/taxAmount/totalAmount arithmetic. Distinct from `discount` above, which does reduce the taxable base."
                },
                "exempt": {
                  "type": "boolean",
                  "description": "When true, the customer claims exemption for this line item. If no active ECM certificate exists for the customer, a PENDING_CERTIFICATE record is created and returned in the response's pendingCertificates[]."
                },
                "exemptionReason": {
                  "type": "string",
                  "enum": [
                    "RESALE",
                    "MANUFACTURING",
                    "AGRICULTURAL",
                    "ENERGY",
                    "EXEMPT_ORG",
                    "GOVERNMENT",
                    "DIRECT_PAY",
                    "BLANKET_OTHER"
                  ],
                  "description": "Self-asserted reason for THIS line's exempt claim, with no certificate on file yet — a subset of ExemptionCertificate.certificateType's vocabulary: excludes DIGITAL_SERVICES_COMMERCIAL_USE and EXPORT_AUTHORIZATION, which are only valid narrowly scoped (Iowa SaaS categories; Ireland Section 56 respectively) and require the real certificate-creation flow's own region/category/form enforcement — not expressible as a bare self-assertion. Only meaningful alongside exempt: true on this same line — supplying it without exempt: true on the same line is a 422. Different lines in one transaction may carry different reasons (e.g. one resale, another government-use). Omitted while exempt: true defaults to BLANKET_OTHER. Echoed back on the response line item as lineItems[].exemptionReason (a plain echo, not nested under `exemption` — it never implies a certificate record exists)."
                },
                "shippingCarrier": {
                  "type": "string",
                  "enum": [
                    "common",
                    "seller_vehicle"
                  ],
                  "description": "Delivery carrier type for a shipping_handling line item — some US states' shipping-taxability statutes carve out delivery by a common/contract carrier from delivery in the seller's own vehicle. Ignored for any line that is not shipping_handling, and for any non-US jurisdiction. Conservative-taxable default: omitted resolves to 'seller_vehicle' wherever this is consulted. An omitted value may first be filled by the account's default shipping carrier rule (template default_shipping_carrier, see GET /rules-engine/templates); a value sent on the line is never overwritten, and the shipping rules always decide the tax."
                },
                "chargeAvoidable": {
                  "type": "boolean",
                  "description": "Whether the customer could have avoided this shipping/handling charge (e.g. in-store pickup) rather than it being a mandatory part of the sale. Some US states condition their separately-stated-shipping exemption on avoidability. Ignored for any line that is not shipping_handling, and for any non-US jurisdiction. Conservative-taxable default: omitted resolves to false (not avoidable). An omitted value may first be filled by the account's default rule (template default_shipping_charge_avoidable); a value sent on the line is never overwritten, and the shipping rules always decide the tax."
                },
                "actualCostOfShipment": {
                  "type": "boolean",
                  "description": "Whether a shipping_handling charge passes through the seller's actual cost of shipment rather than being a flat fee or marked up above cost — some states' transportation-charge carve-outs (e.g. Arizona's LR13-003) require actual cost. Ignored for any line that is not shipping_handling, and for any non-US jurisdiction. Conservative-taxable default: omitted resolves to false (not actual cost). An omitted value may first be filled by the account's default rule (template default_shipping_actual_cost); a value sent on the line is never overwritten, and the shipping rules always decide the tax."
                },
                "recoverablePercentOverride": {
                  "type": "number",
                  "minimum": 0,
                  "maximum": 100,
                  "description": "Purchase mode only (transactionDirection: 'purchase') — ignored for a 'sale' line. Manual override of this line's recoverable input-tax share, 0–100 (not a 0–1 fraction). Wins over whatever recoverability the resolved client tax code carries. Echoed back via lineItems[].recoverability.recoverablePercent."
                },
                "shipTo": {
                  "$ref": "#/components/schemas/TaxAddress",
                  "description": "Purchase mode only. Where THIS line is delivered, overriding the request-level shipTo (country required; region required for US). A US address makes the line US consumer's use tax at that address."
                },
                "statedTaxAmount": {
                  "type": "number",
                  "description": "Purchase mode only (transactionDirection: 'purchase') — ignored for a 'sale' line. The tax amount the supplier actually charged on this line, per their invoice, in the same currency as the calculation. When supplied, compared against the engine's own independently-computed taxAmount and returned as lineItems[].taxVerification — omitted entirely for a border-paid (non-postponed) cross-border import-of-goods line, since EU/UK import VAT is legally assessed on customs value + duty + freight, a base this API has no data for. Omit to skip verification entirely (not defaulted to a MATCH)."
                },
                "supplyType": {
                  "type": "string",
                  "enum": [
                    "GOODS",
                    "SERVICES"
                  ],
                  "description": "Purchase mode only (transactionDirection: 'purchase') — ignored for a 'sale' line. Goods-vs-services signal for this purchase line — the coarse binary a vendor bill actually carries, distinct from the taxCategory/place-of-supply vocabulary elsewhere in this API. A purchase line with no supplyType still returns a full treatment/rate, but is flagged lineItems[].outcome: { status: 'UNKNOWN_TREATMENT', reasonCode: 'missing_supply_type' } rather than treated as fully resolved."
                },
                "purchaseOrderId": {
                  "type": "string",
                  "description": "Rules-engine (M.14/RE-6) — this line's own purchase-order header id, for documentStage: 'invoice' to link back against a previously calculated documentStage: 'purchase_order' line."
                },
                "purchaseOrderLineNumber": {
                  "type": "string",
                  "description": "Which PO line poLink resolves against, when the PO header may omit it. Ignored when purchaseOrderId is absent."
                },
                "glAccount": {
                  "type": "string",
                  "maxLength": 50,
                  "description": "Purchase side. The general-ledger account this line is booked to. Free text. Readable and writable by rules as the built-in property `glAccount`; echoed post-rule on the response line."
                },
                "costCenter": {
                  "type": "string",
                  "maxLength": 50,
                  "description": "Purchase side. The cost centre this line is booked to. Free text. Readable and writable by rules as `costCenter`; echoed post-rule on the response line."
                },
                "intendedUse": {
                  "type": "string",
                  "maxLength": 100,
                  "description": "Purchase side. What the buyer intends to use the goods or services for, free text (for example `resale`, `manufacturing`, `raw material`). Used to match a buyer-held exemption certificate to the line. Readable and writable by rules as `intendedUse`; echoed post-rule."
                },
                "accountAssignment": {
                  "type": "string",
                  "maxLength": 50,
                  "description": "Purchase side. How the line is assigned in the buyer's books: `stock`, `expense`, `asset` or `project`. A raw ERP code (a SAP KNTTP letter such as K, A, P, F, C, Q, E or N, or a Coupa account-type string such as capital or inventory) is also accepted on input and is mapped onto those four by the `account_assignment_map` reference dataset; a code with no mapping is dropped and the `AP_ACCOUNT_ASSIGNMENT_UNKNOWN` warning fires. The response always echoes one of the four. `stock` marks a resale line for buyer-held certificate matching. Readable and writable by rules as `accountAssignment`."
                },
                "customProperties": {
                  "type": "object",
                  "additionalProperties": true,
                  "description": "Per-line open, customer-defined fact bag, for data that is NOT part of the platform schema. Every key must be defined first via POST /rules-engine/properties; an undefined key is a 400 `UNKNOWN_CUSTOM_PROPERTY`. A key that names a built-in field (`glAccount`, `costCenter`, `intendedUse`, `accountAssignment`, `commodityCode`, `commodityCodeScheme`, ...) is a 400 `BUILT_IN_PROPERTY_AS_CUSTOM`: send it as the regular line field. Values are string, number or boolean."
                }
              }
            }
          },
          "evidence": {
            "type": "object",
            "description": "Additional signals for B2C jurisdiction resolution.",
            "properties": {
              "ipAddress": {
                "type": "string",
                "example": "203.0.113.1"
              },
              "binCountry": {
                "type": "string",
                "description": "ISO country code from the payment card BIN.",
                "example": "US"
              }
            }
          },
          "documentStage": {
            "type": "string",
            "enum": [
              "purchase_order",
              "invoice"
            ],
            "description": "Rules-engine (M.14/RE-6) — which stage of a purchase document this calculation represents. Only meaningful on transactionDirection: 'purchase'. Enterprise only (Purchases (AP) sub-solution): `purchase_order` is refused with 403 `ap_not_included` or `ap_not_enabled` without it."
          },
          "customProperties": {
            "type": "object",
            "additionalProperties": true,
            "description": "Header-level open, customer-defined fact bag, for data that is NOT part of the platform schema. A rules-engine condition/action reads and writes a key as customProperties.<key>. Every key must be defined first via POST /rules-engine/properties (GET /rules-engine/properties lists this key's own scope); an undefined key is a 400 `UNKNOWN_CUSTOM_PROPERTY`. A key that names a built-in field (`glAccount`, `costCenter`, `intendedUse`, `accountAssignment`, `commodityCode`, `commodityCodeScheme`, ...) is a 400 `BUILT_IN_PROPERTY_AS_CUSTOM`: send it as the regular line field."
          }
        }
      },
      "TaxCalcParty": {
        "type": "object",
        "description": "One resolved party (`supplier` or `customer`) on a TaxCalculateResponse. The entity-side block never carries validation fields; the counterparty block additionally carries b2bOverride/taxIdValidated/taxIdValidationStatus. See TaxCalculateResponse.entityRole for which block is which.",
        "properties": {
          "country": {
            "type": "string",
            "nullable": true,
            "description": "This party's resolved country."
          },
          "region": {
            "type": "string",
            "description": "State/region code, when resolved (e.g. a US party). Omitted (not null) when unknown."
          },
          "postalCode": {
            "type": "string",
            "description": "Omitted (not null) when unknown."
          },
          "taxId": {
            "type": "string",
            "description": "This party's tax/VAT ID, when known. Omitted (not null) when unknown."
          },
          "isEntity": {
            "type": "boolean",
            "description": "True when this block is your own entity (auto-enriched from master data, or matched against it if you supplied it); false when it is the counterparty."
          },
          "b2bOverride": {
            "type": "boolean",
            "description": "Present only on the counterparty block. Echoes the request's b2bOverride, if supplied."
          },
          "taxIdValidated": {
            "type": "boolean",
            "description": "Present only on the counterparty block. True when the supplied taxId passed live-authority (e.g. VIES) verification."
          },
          "taxIdValidationStatus": {
            "type": "string",
            "description": "Present only on the counterparty block. Outcome of taxId verification, e.g. VALID, INVALID, UNVERIFIED, SKIPPED."
          }
        }
      },
      "DutyRateExpression": {
        "description": "How a duty measure is charged, as a closed union discriminated by `type`. Ad valorem values are percent (16.5 = 16.5%). Nested nodes (compound, alternative, min_max) go at most six levels deep. The root node may also carry `not_if_measure_type_in`: the measure is dropped when another measure of one of those types applies to the same line.",
        "oneOf": [
          {
            "type": "object",
            "required": [
              "type",
              "value"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "ad_valorem_pct"
                ]
              },
              "value": {
                "type": "number",
                "minimum": 0,
                "maximum": 1000,
                "description": "Percent of customs value."
              },
              "not_if_measure_type_in": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          {
            "type": "object",
            "required": [
              "type",
              "value"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "top_up_to_pct"
                ]
              },
              "value": {
                "type": "number",
                "minimum": 0,
                "maximum": 1000,
                "description": "Target percent; the additional duty is the amount by which it exceeds the ad valorem percent the base measures already apply."
              },
              "not_if_measure_type_in": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          {
            "type": "object",
            "required": [
              "type",
              "amount",
              "currency"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "flat"
                ]
              },
              "amount": {
                "type": "number",
                "minimum": 0
              },
              "currency": {
                "type": "string",
                "pattern": "^[A-Z]{3}$"
              },
              "not_if_measure_type_in": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          {
            "type": "object",
            "required": [
              "type",
              "amount",
              "currency",
              "per"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "specific"
                ]
              },
              "amount": {
                "type": "number",
                "minimum": 0
              },
              "currency": {
                "type": "string",
                "pattern": "^[A-Z]{3}$"
              },
              "per": {
                "type": "string",
                "description": "Unit the amount is charged per, e.g. kg, 100kg, item, litre.",
                "example": "kg"
              },
              "not_if_measure_type_in": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          {
            "type": "object",
            "required": [
              "type",
              "parts"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "compound"
                ]
              },
              "parts": {
                "type": "array",
                "minItems": 2,
                "items": {
                  "$ref": "#/components/schemas/DutyRateExpression"
                },
                "description": "Summed."
              },
              "not_if_measure_type_in": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          {
            "type": "object",
            "required": [
              "type",
              "mode",
              "options"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "alternative"
                ]
              },
              "mode": {
                "type": "string",
                "enum": [
                  "greater_of",
                  "lesser_of"
                ]
              },
              "options": {
                "type": "array",
                "minItems": 2,
                "items": {
                  "$ref": "#/components/schemas/DutyRateExpression"
                }
              },
              "not_if_measure_type_in": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          {
            "type": "object",
            "required": [
              "type",
              "base"
            ],
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "min_max"
                ]
              },
              "base": {
                "$ref": "#/components/schemas/DutyRateExpression"
              },
              "min": {
                "$ref": "#/components/schemas/DutyRateExpression"
              },
              "max": {
                "$ref": "#/components/schemas/DutyRateExpression"
              },
              "not_if_measure_type_in": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            },
            "description": "At least one of min and max is required."
          }
        ]
      },
      "DutyNotEvaluated": {
        "type": "object",
        "description": "A measure family the engine knows applies or might apply but could not evaluate. A consignment with any entry here is never collected from the buyer.",
        "required": [
          "familyCode",
          "measureType",
          "reason"
        ],
        "properties": {
          "familyCode": {
            "type": "string",
            "example": "US_301_BY_ORIGIN_CN"
          },
          "measureType": {
            "type": "string",
            "example": "SECTION_301_BY_ORIGIN"
          },
          "reason": {
            "type": "string",
            "enum": [
              "not_in_force",
              "origin_unknown",
              "invalid_rate_expression"
            ]
          },
          "legalStatus": {
            "type": "string",
            "description": "The measure's legal status when reason is not_in_force, e.g. announced.",
            "example": "announced"
          }
        }
      },
      "DutyMeasure": {
        "type": "object",
        "description": "One duty measure applied to a line. `type` is content-driven and open (MFN, SECTION_301_LIST_4A, ANTI_DUMPING, ...); `rateExpression` is the closed union.",
        "required": [
          "type",
          "rate",
          "rateExpression",
          "amount",
          "legalBasis",
          "effectiveFrom"
        ],
        "properties": {
          "type": {
            "type": "string",
            "example": "MFN"
          },
          "rate": {
            "type": "number",
            "nullable": true,
            "description": "Decimal fraction (0.165 = 16.5%) for a pure ad valorem expression; null for specific, compound, alternative and capped forms.",
            "example": 0.165
          },
          "rateExpression": {
            "$ref": "#/components/schemas/DutyRateExpression"
          },
          "amount": {
            "type": "number",
            "description": "Duty in the transaction currency, rounded to its minor unit.",
            "example": 6.6
          },
          "legalBasis": {
            "type": "string",
            "nullable": true,
            "example": "HTSUS 9903.88.15"
          },
          "effectiveFrom": {
            "type": "string",
            "format": "date"
          }
        }
      },
      "LineDuty": {
        "type": "object",
        "description": "Estimated duty for one line. Never part of the line's tax.",
        "required": [
          "consignmentId",
          "commodityCode",
          "scheme",
          "codeSource",
          "precision",
          "origin",
          "originSource",
          "customsValue",
          "amount",
          "measures",
          "notEvaluated",
          "missingInputs",
          "estimateReasons"
        ],
        "properties": {
          "consignmentId": {
            "type": "string",
            "example": "c1"
          },
          "commodityCode": {
            "type": "string",
            "nullable": true,
            "description": "The code the duty was priced at; a platform or proxy code when the request carried none. Digits only is not guaranteed; the code is as stored in the tariff."
          },
          "scheme": {
            "type": "string",
            "nullable": true,
            "enum": [
              "HS6",
              "CN8",
              "TARIC10",
              "UK10",
              "HTS10",
              null
            ]
          },
          "schemeInferred": {
            "type": "boolean",
            "enum": [
              true
            ],
            "description": "Present (true) only when the caller omitted commodityCodeScheme and it was inferred from the digit count and the destination."
          },
          "codeSource": {
            "type": "string",
            "enum": [
              "request",
              "catalogue",
              "platform_code",
              "category_proxy",
              "chapter_proxy",
              "none"
            ],
            "description": "Where the code came from: the request, the product catalogue, a platform code (classificationCodes), a category proxy (from the tax category) or a chapter proxy."
          },
          "precision": {
            "type": "string",
            "nullable": true,
            "enum": [
              "TARIFF_LINE",
              "HS6_EXACT",
              "HS6_AMBIGUOUS",
              "PLATFORM_CODE",
              "CATEGORY_PROXY",
              "CHAPTER_PROXY",
              "UNRESOLVED",
              null
            ],
            "description": "How exactly the line was priced. TARIFF_LINE is exact; HS6_EXACT/HS6_AMBIGUOUS price at six digits (modal or highest rate per ratePolicyForHs6); PLATFORM_CODE came through classificationCodes; CATEGORY_PROXY, CHAPTER_PROXY and UNRESOLVED are never collected from a buyer. Null when a flat or zero regime set the duty."
          },
          "origin": {
            "type": "string",
            "nullable": true,
            "description": "ISO 3166-1 alpha-2 country of origin used.",
            "example": "CN"
          },
          "originSource": {
            "type": "string",
            "enum": [
              "request",
              "catalogue",
              "entity_default",
              "unknown"
            ]
          },
          "customsValue": {
            "type": "number",
            "description": "Line customs value in the transaction currency (FOB or CIF per the destination rule).",
            "example": 40
          },
          "amount": {
            "type": "number",
            "description": "Sum of measures[].amount, or the regime's allocated flat amount.",
            "example": 9.6
          },
          "measures": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DutyMeasure"
            }
          },
          "notEvaluated": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DutyNotEvaluated"
            }
          },
          "policyApplied": {
            "type": "string",
            "enum": [
              "modal",
              "highest"
            ],
            "description": "Present when a six-digit code spanned several national tariff lines."
          },
          "rateRange": {
            "type": "object",
            "properties": {
              "min": {
                "type": "number"
              },
              "max": {
                "type": "number"
              }
            },
            "description": "Lowest and highest ad valorem rate across the national lines under an HS6 code."
          },
          "missingInputs": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Inputs that would tighten the estimate, e.g. weight or quantity for a specific duty."
          },
          "estimateReasons": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "precision_below_tariff_line",
                "origin_unknown",
                "origin_measures_apply",
                "fx_static",
                "measure_not_in_force",
                "preference_not_considered",
                "entry_type_assumed",
                "regime_threshold_near_boundary",
                "tax_treatment_mismatch",
                "quantity_missing",
                "b2c_personal_value_factor",
                "import_tax_rate_missing",
                "delegated_fee_missing",
                "shipping_mode_assumed",
                "valuation_rule_unverified",
                "import_tax_region_assumed",
                "responsible_party_unresolved"
              ]
            }
          }
        }
      },
      "DutyConsignment": {
        "description": "One consignment: the goods that cross one customs border together. Discriminated by `status`.",
        "oneOf": [
          {
            "$ref": "#/components/schemas/DutyConsignmentQuoted"
          },
          {
            "$ref": "#/components/schemas/DutyConsignmentDegraded"
          },
          {
            "$ref": "#/components/schemas/DutyConsignmentNotApplicable"
          }
        ],
        "discriminator": {
          "propertyName": "status",
          "mapping": {
            "quoted": "#/components/schemas/DutyConsignmentQuoted",
            "degraded": "#/components/schemas/DutyConsignmentDegraded",
            "not_applicable": "#/components/schemas/DutyConsignmentNotApplicable"
          }
        }
      },
      "DutyConsignmentNotApplicable": {
        "type": "object",
        "required": [
          "id",
          "lineIds",
          "status",
          "reason",
          "dispatchTerritory",
          "destinationTerritory"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "lineIds": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "not_applicable"
            ]
          },
          "reason": {
            "type": "string",
            "enum": [
              "no_customs_crossing",
              "ship_from_unknown"
            ]
          },
          "dispatchTerritory": {
            "type": "string",
            "nullable": true
          },
          "destinationTerritory": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "DutyConsignmentDegraded": {
        "type": "object",
        "description": "The consignment could not be priced. Tax is unaffected.",
        "required": [
          "id",
          "lineIds",
          "status",
          "reason",
          "dispatchTerritory",
          "destinationTerritory",
          "customsStatus",
          "collectedAtCheckout",
          "estimate"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "lineIds": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "degraded"
            ]
          },
          "reason": {
            "type": "string",
            "enum": [
              "fx_rate_missing",
              "territory_content_missing",
              "content_unavailable",
              "internal_error"
            ]
          },
          "dispatchTerritory": {
            "type": "string",
            "nullable": true
          },
          "destinationTerritory": {
            "type": "string",
            "nullable": true
          },
          "customsStatus": {
            "type": "string",
            "enum": [
              "free_circulation",
              "bonded"
            ]
          },
          "collectedAtCheckout": {
            "type": "boolean",
            "enum": [
              false
            ]
          },
          "estimate": {
            "type": "boolean",
            "enum": [
              true
            ]
          }
        }
      },
      "DutyConsignmentQuoted": {
        "type": "object",
        "description": "A priced consignment. Everything here is an estimate and none of it is tax.",
        "required": [
          "id",
          "lineIds",
          "status",
          "dispatchTerritory",
          "destinationTerritory",
          "customsStatus",
          "valuation",
          "regime",
          "duty",
          "importTax",
          "fees",
          "responsibleParty",
          "responsiblePartySource",
          "collectedAtCheckout",
          "collection",
          "landedCost",
          "importChargesAtCheckout",
          "sellerBorne",
          "precision",
          "estimate",
          "estimateReasons",
          "notEvaluated",
          "fxSource",
          "sellerRegistrationGap"
        ],
        "properties": {
          "id": {
            "type": "string",
            "example": "c1"
          },
          "lineIds": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "quoted"
            ]
          },
          "dispatchTerritory": {
            "type": "string",
            "nullable": true,
            "description": "Customs territory goods leave from (e.g. GB, EU, US).",
            "example": "GB"
          },
          "destinationTerritory": {
            "type": "string",
            "nullable": true,
            "example": "US"
          },
          "customsStatus": {
            "type": "string",
            "enum": [
              "free_circulation",
              "bonded"
            ]
          },
          "valuation": {
            "type": "object",
            "nullable": true,
            "properties": {
              "basis": {
                "type": "string",
                "enum": [
                  "FOB",
                  "CIF"
                ],
                "description": "FOB: freight and insurance are not part of customs value (US). CIF: they are (EU, UK)."
              },
              "goods": {
                "type": "number"
              },
              "freight": {
                "type": "number"
              },
              "insurance": {
                "type": "number"
              },
              "customsValue": {
                "type": "number"
              },
              "currency": {
                "type": "string"
              }
            }
          },
          "regime": {
            "type": "object",
            "nullable": true,
            "description": "The low-value regime that applied (EU IOSS or Special Arrangements up to EUR 150, UK GBP 135, ...), when one did.",
            "properties": {
              "code": {
                "type": "string",
                "example": "EU_IOSS_150"
              },
              "name": {
                "type": "string"
              },
              "basis": {
                "type": "string",
                "enum": [
                  "intrinsic_value",
                  "consignment_value"
                ]
              },
              "thresholdAmount": {
                "type": "number"
              },
              "thresholdCurrency": {
                "type": "string"
              },
              "comparedValue": {
                "type": "number"
              },
              "nearThreshold": {
                "type": "boolean"
              },
              "consequence": {
                "type": "object",
                "properties": {
                  "vat": {
                    "type": "string",
                    "enum": [
                      "checkout",
                      "border",
                      "none"
                    ]
                  },
                  "duty": {
                    "type": "string",
                    "enum": [
                      "tariff",
                      "zero",
                      "flat_per_item",
                      "flat_per_order"
                    ]
                  }
                }
              },
              "feeCode": {
                "type": "string",
                "nullable": true
              },
              "shippingMode": {
                "type": "object",
                "description": "Present when the destination has mode-limited regimes: the mode the regime was chosen under and whether it was assumed because the request carried no shippingMode.",
                "properties": {
                  "mode": {
                    "type": "string",
                    "enum": [
                      "post",
                      "courier"
                    ],
                    "nullable": true
                  },
                  "assumed": {
                    "type": "boolean"
                  }
                }
              },
              "taxTreatmentMismatch": {
                "type": "boolean",
                "description": "True when the tax engine's treatment disagrees with the regime (for example the seller is not IOSS-registered)."
              },
              "mismatchReason": {
                "type": "string",
                "enum": [
                  "shipping_in_tax_threshold",
                  "fx_nominal_in_tax_threshold",
                  "seller_not_ioss_registered"
                ]
              },
              "notes": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          "duty": {
            "type": "object",
            "required": [
              "total",
              "source",
              "estimate"
            ],
            "properties": {
              "total": {
                "type": "number",
                "example": 9.6
              },
              "source": {
                "type": "string",
                "enum": [
                  "tariff",
                  "regime",
                  "fee_rule"
                ]
              },
              "feeCode": {
                "type": "string"
              },
              "estimate": {
                "type": "boolean"
              }
            }
          },
          "importTax": {
            "type": "object",
            "nullable": true,
            "description": "Import VAT/GST/consumption tax estimate. Under IOSS the VAT is already in totalTax, so amount is 0 with collection 'checkout'.",
            "properties": {
              "label": {
                "type": "string",
                "nullable": true,
                "example": "VAT"
              },
              "collection": {
                "type": "string",
                "enum": [
                  "checkout",
                  "border",
                  "uncollected",
                  "none"
                ]
              },
              "base": {
                "type": "number"
              },
              "rate": {
                "type": "number",
                "nullable": true,
                "description": "The single rate when every line shares it; null when lines differ or the destination rate could not be resolved (see notes)."
              },
              "amount": {
                "type": "number"
              },
              "baseIncludes": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "example": [
                  "customs_value",
                  "duty",
                  "freight"
                ]
              },
              "sellerRegistrationRequired": {
                "type": "boolean"
              },
              "notes": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          "fees": {
            "type": "array",
            "description": "Customs-authority fees (US Merchandise Processing Fee, ...). A fee is a seller cost and never part of the buyer's amount due.",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string",
                  "example": "US_MPF_INFORMAL"
                },
                "label": {
                  "type": "string"
                },
                "amount": {
                  "type": "number"
                },
                "currency": {
                  "type": "string"
                },
                "basis": {
                  "type": "string",
                  "enum": [
                    "flat",
                    "percent",
                    "percent_minimum",
                    "percent_maximum"
                  ]
                },
                "entryType": {
                  "type": "string",
                  "nullable": true,
                  "enum": [
                    "formal",
                    "informal",
                    null
                  ]
                },
                "entryTypeAssumed": {
                  "type": "boolean",
                  "description": "True when the entry type was inferred from the consignment value rather than stated."
                },
                "incidence": {
                  "type": "string",
                  "example": "SELLER_COST"
                }
              }
            }
          },
          "responsibleParty": {
            "type": "string",
            "enum": [
              "seller",
              "customer",
              "unresolved"
            ],
            "description": "Who is the importer of record: the request's importerOfRecord, else incoterms (DDP is the seller, any other rule the customer), else the account's defaultImporterOfRecord, else unresolved. Under IOSS the seller is the declarant."
          },
          "responsiblePartySource": {
            "type": "string",
            "enum": [
              "request",
              "incoterms",
              "entityDefault",
              "regime",
              "unresolved"
            ]
          },
          "collectedAtCheckout": {
            "type": "boolean",
            "description": "Short form of collection.collected."
          },
          "collection": {
            "type": "object",
            "required": [
              "requested",
              "collected",
              "reason"
            ],
            "properties": {
              "requested": {
                "type": "boolean"
              },
              "collected": {
                "type": "boolean"
              },
              "reason": {
                "type": "string",
                "enum": [
                  "collected",
                  "not_requested",
                  "precision_too_low",
                  "seller_responsible",
                  "importer_of_record_unresolved",
                  "regime_vat_at_checkout",
                  "measures_not_evaluated"
                ]
              }
            }
          },
          "landedCost": {
            "type": "number",
            "description": "goods + freight + insurance + duty + import tax + fees. Checkout VAT already in totalTax is not added again. A pricing figure, never a charge.",
            "example": 52.37
          },
          "importChargesAtCheckout": {
            "type": "number",
            "description": "The buyer-payable estimate a checkout may add for this consignment; 0 unless collectedAtCheckout."
          },
          "sellerBorne": {
            "type": "object",
            "properties": {
              "duty": {
                "type": "number"
              },
              "importTax": {
                "type": "number"
              },
              "fees": {
                "type": "number"
              },
              "total": {
                "type": "number"
              }
            }
          },
          "precision": {
            "type": "string",
            "nullable": true,
            "enum": [
              "TARIFF_LINE",
              "HS6_EXACT",
              "HS6_AMBIGUOUS",
              "PLATFORM_CODE",
              "CATEGORY_PROXY",
              "CHAPTER_PROXY",
              "UNRESOLVED",
              null
            ],
            "description": "The worst line precision among lines priced from the tariff; null when a flat or zero regime set the duty."
          },
          "estimate": {
            "type": "boolean",
            "description": "false means only that there is no modelled source of variance (a flat or zero regime, or tariff-line precision with no origin-scoped measure and no static-FX use). It is never a guarantee: the declarant's classification, valuation, origin and declaration choices prevail."
          },
          "estimateReasons": {
            "type": "array",
            "description": "Empty if and only if estimate is false.",
            "items": {
              "type": "string",
              "enum": [
                "precision_below_tariff_line",
                "origin_unknown",
                "origin_measures_apply",
                "fx_static",
                "measure_not_in_force",
                "preference_not_considered",
                "entry_type_assumed",
                "regime_threshold_near_boundary",
                "tax_treatment_mismatch",
                "quantity_missing",
                "b2c_personal_value_factor",
                "import_tax_rate_missing",
                "delegated_fee_missing",
                "shipping_mode_assumed",
                "valuation_rule_unverified",
                "import_tax_region_assumed",
                "responsible_party_unresolved"
              ]
            }
          },
          "notEvaluated": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DutyNotEvaluated"
            }
          },
          "fxSource": {
            "type": "string",
            "nullable": true,
            "enum": [
              "static",
              null
            ],
            "description": "'static' when an amount used the static FX table, which is not live."
          },
          "sellerRegistrationGap": {
            "type": "boolean",
            "description": "True when the destination requires the seller to hold a registration they do not have."
          }
        }
      },
      "DutiesBlock": {
        "type": "object",
        "description": "Outcome of the duty calculation. A duty failure sets status 'degraded' here and never sets the calculation's own `degraded`.",
        "required": [
          "status"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "quoted",
              "not_applicable",
              "degraded",
              "disabled"
            ],
            "description": "quoted: duties were calculated. not_applicable: no customs crossing, no goods lines, or a credit note. degraded: the duty stage could not complete. disabled: the cart carries duty inputs but duties are off for this account and request."
          },
          "reason": {
            "type": "string",
            "enum": [
              "no_customs_crossing",
              "ship_from_unknown",
              "no_goods_lines",
              "credit_note",
              "classification_unresolved",
              "not_enabled",
              "fx_rate_missing",
              "territory_content_missing",
              "content_unavailable",
              "internal_error"
            ],
            "description": "Present when status is not 'quoted'."
          },
          "contentVersion": {
            "type": "integer",
            "description": "The duty content version the quote was computed from, for reproducibility. Omitted when degraded or not applicable."
          },
          "fx": {
            "type": "object",
            "description": "Present when any amount used the static FX table.",
            "properties": {
              "source": {
                "type": "string",
                "enum": [
                  "static"
                ]
              }
            }
          }
        }
      },
      "DutiesSummary": {
        "type": "object",
        "description": "Duty totals across the quoted consignments. Estimates; none of it is part of totalTax or totalAmountWithTax.",
        "properties": {
          "totalDuty": {
            "type": "number"
          },
          "totalImportTax": {
            "type": "number"
          },
          "totalImportFees": {
            "type": "number"
          },
          "totalLandedCost": {
            "type": "number",
            "description": "goods + freight + insurance + duty + import tax + authority fees. A merchant pricing figure, never a charge."
          },
          "sellerBorneImportCosts": {
            "type": "number",
            "description": "Duty, import tax and fees the seller bears (seller-responsible consignments); never part of any amount due."
          },
          "importChargesAtCheckout": {
            "type": "number",
            "description": "Present only when some consignment is collectedAtCheckout. Show it as its own line, \"Estimated import charges\" (duties, taxes and fees payable on importation); never as tax. Refund any excess."
          },
          "totalAmountDue": {
            "type": "number",
            "description": "Present only alongside importChargesAtCheckout: totalAmountWithTax + importChargesAtCheckout. Charge this when present."
          }
        }
      },
      "DutiesResult": {
        "type": "object",
        "description": "Estimated import duty, import VAT/GST and customs fees for a cross-border consignment. Never included in totalTax/totalAmountWithTax; see summary.importChargesAtCheckout for the one amount a checkout may add. The response of POST /duties/quote; on POST /tax/calculate the same blocks are `duties`, `consignments`, `lineItems[].duty` and the duty fields of `summary`. Every figure is an estimate and Clearvo does not guarantee it: reconcile any difference with the carrier or broker invoice.",
        "required": [
          "currency",
          "duties",
          "lineItems"
        ],
        "properties": {
          "currency": {
            "type": "string",
            "description": "Every amount is in this currency (the request's currency).",
            "example": "USD"
          },
          "duties": {
            "$ref": "#/components/schemas/DutiesBlock"
          },
          "consignments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DutyConsignment"
            }
          },
          "lineItems": {
            "type": "array",
            "description": "Only the lines that carry a duty block.",
            "items": {
              "type": "object",
              "required": [
                "id",
                "duty"
              ],
              "properties": {
                "id": {
                  "type": "string",
                  "description": "Your line id, echoed."
                },
                "duty": {
                  "$ref": "#/components/schemas/LineDuty"
                }
              }
            }
          },
          "summary": {
            "$ref": "#/components/schemas/DutiesSummary"
          }
        }
      },
      "DutiesEstimateRequest": {
        "type": "object",
        "description": "One product priced across up to 20 destinations. Identify the product with at least one of `commodityCode` (with `commodityCodeScheme`), `classificationCodes`, `productCode`/`sku` (resolved through the catalogue) or `taxCategory`/`slug`, and price it with `unitPrice` (times `quantity`) or `amount`. Unknown fields are rejected with 422. `supplier`, `sellerRegistrationContext` and `partnerMerchantId` are for a `partner_calc` key only (required there, as on POST /tax/calculate/partner) and rejected for an entity key.",
        "required": [
          "currency",
          "product",
          "destinations"
        ],
        "properties": {
          "currency": {
            "type": "string",
            "description": "ISO 4217. The price, freight and insurance are in it and every amount returned is in it.",
            "example": "USD"
          },
          "product": {
            "type": "object",
            "required": [],
            "description": "The product. At least one identifier is required, and exactly one of `amount` and `unitPrice`.",
            "properties": {
              "commodityCode": {
                "type": "string",
                "maxLength": 40,
                "example": "610910"
              },
              "commodityCodeScheme": {
                "type": "string",
                "enum": [
                  "HS6",
                  "CN8",
                  "TARIC10",
                  "UK10",
                  "HTS10"
                ],
                "description": "Scheme of `commodityCode`. Inferred from its length when omitted (the result then carries `schemeInferred`)."
              },
              "classificationCodes": {
                "type": "array",
                "maxItems": 5,
                "items": {
                  "type": "object",
                  "required": [
                    "system",
                    "code"
                  ],
                  "properties": {
                    "system": {
                      "type": "string",
                      "example": "hs"
                    },
                    "code": {
                      "type": "string"
                    }
                  }
                }
              },
              "productCode": {
                "type": "string",
                "maxLength": 40,
                "description": "Catalogue product code: its stored commodity code and origin are used when the request carries none."
              },
              "sku": {
                "type": "string",
                "maxLength": 40,
                "description": "Alias of `productCode`."
              },
              "taxCategory": {
                "type": "string",
                "maxLength": 100,
                "description": "Tax category slug: a category-level proxy when no code resolves (lower precision)."
              },
              "slug": {
                "type": "string",
                "maxLength": 100,
                "description": "Alias of `taxCategory`."
              },
              "productName": {
                "type": "string",
                "maxLength": 200
              },
              "countryOfOrigin": {
                "type": "string",
                "minLength": 2,
                "maxLength": 2,
                "description": "ISO 3166-1 alpha-2. Never inferred from `shipFrom`; a missing origin lowers precision.",
                "example": "CN"
              },
              "unitPrice": {
                "type": "number",
                "minimum": 0,
                "description": "Price of one unit, excluding tax. Use this or `amount`."
              },
              "quantity": {
                "type": "number",
                "exclusiveMinimum": 0,
                "description": "Units. The line total is `unitPrice` times `quantity` (1 when omitted); with `amount` it is the unit count for per-unit duty measures."
              },
              "amount": {
                "type": "number",
                "minimum": 0,
                "description": "Line total, excluding tax. Use this or `unitPrice`."
              },
              "weight": {
                "type": "object",
                "description": "Per-UNIT weight.",
                "required": [
                  "value",
                  "unit"
                ],
                "properties": {
                  "value": {
                    "type": "number",
                    "minimum": 0
                  },
                  "unit": {
                    "type": "string",
                    "enum": [
                      "kg",
                      "g",
                      "lb",
                      "oz"
                    ]
                  }
                }
              }
            }
          },
          "shipFrom": {
            "type": "object",
            "description": "Dispatch address. `country` is needed for any duty to be quoted.",
            "properties": {
              "country": {
                "type": "string"
              },
              "region": {
                "type": "string"
              },
              "postalCode": {
                "type": "string"
              },
              "customsStatus": {
                "type": "string",
                "enum": [
                  "free_circulation",
                  "bonded"
                ]
              }
            }
          },
          "incoterms": {
            "type": "string",
            "enum": [
              "EXW",
              "FCA",
              "FAS",
              "FOB",
              "CFR",
              "CIF",
              "CPT",
              "CIP",
              "DAP",
              "DPU",
              "DDP"
            ]
          },
          "importerOfRecord": {
            "type": "string",
            "enum": [
              "SELLER",
              "BUYER"
            ]
          },
          "insurance": {
            "type": "object",
            "required": [
              "amount"
            ],
            "properties": {
              "amount": {
                "type": "number",
                "minimum": 0
              },
              "currency": {
                "type": "string",
                "description": "Defaults to the request currency."
              }
            }
          },
          "freight": {
            "type": "object",
            "required": [
              "amount"
            ],
            "description": "Freight charged to the destination, in the request currency.",
            "properties": {
              "amount": {
                "type": "number",
                "minimum": 0
              }
            }
          },
          "duties": {
            "type": "object",
            "description": "Estimate policy. `include` is implied.",
            "properties": {
              "defaultCountryOfOrigin": {
                "type": "string"
              },
              "ratePolicyForHs6": {
                "type": "string",
                "enum": [
                  "modal",
                  "highest"
                ]
              }
            }
          },
          "destinations": {
            "type": "array",
            "minItems": 1,
            "maxItems": 20,
            "description": "The destinations, without repeats.",
            "items": {
              "type": "object",
              "required": [
                "country"
              ],
              "properties": {
                "country": {
                  "type": "string",
                  "minLength": 2,
                  "maxLength": 2,
                  "example": "AU"
                },
                "region": {
                  "type": "string",
                  "description": "State or province; required for a US destination."
                },
                "postalCode": {
                  "type": "string"
                }
              }
            }
          },
          "supplier": {
            "type": "object",
            "description": "partner_calc keys only: the merchant (same shape as `supplier` on POST /tax/calculate/partner)."
          },
          "sellerRegistrationContext": {
            "type": "object",
            "description": "partner_calc keys only (required there)."
          },
          "partnerMerchantId": {
            "type": "string",
            "description": "partner_calc keys only (required there)."
          }
        }
      },
      "DutiesEstimateProduct": {
        "type": "object",
        "description": "What the product resolved to. Taken from the first destination that produced a duty for it; every destination's own `duty` carries the same fields for that destination.",
        "required": [
          "commodityCode",
          "scheme",
          "codeSource",
          "precision",
          "countryOfOrigin",
          "originSource"
        ],
        "properties": {
          "commodityCode": {
            "type": "string",
            "nullable": true
          },
          "scheme": {
            "type": "string",
            "nullable": true
          },
          "codeSource": {
            "type": "string",
            "enum": [
              "request",
              "catalogue",
              "platform_code",
              "category_proxy",
              "chapter_proxy",
              "none"
            ]
          },
          "precision": {
            "type": "string",
            "nullable": true,
            "enum": [
              "TARIFF_LINE",
              "HS6_EXACT",
              "HS6_AMBIGUOUS",
              "PLATFORM_CODE",
              "CATEGORY_PROXY",
              "CHAPTER_PROXY",
              "UNRESOLVED",
              null
            ],
            "description": "Same vocabulary as `LineDuty.precision`."
          },
          "countryOfOrigin": {
            "type": "string",
            "nullable": true
          },
          "originSource": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "DutiesEstimateDestination": {
        "type": "object",
        "description": "One destination's estimate. The `duties`, `consignments` and `summary` blocks are exactly what POST /duties/quote returns for the one-line cart.",
        "required": [
          "destination",
          "status",
          "duties"
        ],
        "properties": {
          "destination": {
            "type": "object",
            "required": [
              "country"
            ],
            "properties": {
              "country": {
                "type": "string"
              },
              "region": {
                "type": "string"
              },
              "postalCode": {
                "type": "string"
              }
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "quoted",
              "degraded",
              "not_applicable"
            ],
            "description": "quoted: duties were estimated. not_applicable: nothing crosses a customs border (see `reason`). degraded: this destination could not be estimated (`reason` `destination_rejected`: the engine refused it, see `error`; `internal_error`; or the duty stage's own degraded reason). It never fails the call."
          },
          "reason": {
            "type": "string",
            "description": "Present unless `status` is quoted."
          },
          "error": {
            "type": "string",
            "description": "Why a rejected destination was refused (for example a US destination without a region)."
          },
          "duties": {
            "$ref": "#/components/schemas/DutiesBlock"
          },
          "consignments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DutyConsignment"
            }
          },
          "duty": {
            "$ref": "#/components/schemas/LineDuty"
          },
          "summary": {
            "$ref": "#/components/schemas/DutiesSummary"
          }
        }
      },
      "DutiesEstimateResult": {
        "type": "object",
        "description": "The response of POST /duties/estimate. Every figure is an estimate and none of it is tax. Nothing is stored and there is no calculation id.",
        "required": [
          "currency",
          "product",
          "destinations"
        ],
        "properties": {
          "currency": {
            "type": "string",
            "example": "USD"
          },
          "product": {
            "$ref": "#/components/schemas/DutiesEstimateProduct"
          },
          "destinations": {
            "type": "array",
            "description": "In request order.",
            "items": {
              "$ref": "#/components/schemas/DutiesEstimateDestination"
            }
          }
        }
      },
      "ImportSettlementRequest": {
        "type": "object",
        "description": "What customs or the carrier actually charged. Unknown fields are rejected with 422.",
        "required": [
          "entryNumber",
          "actual"
        ],
        "properties": {
          "entryNumber": {
            "type": "string",
            "maxLength": 100,
            "description": "The customs entry or declaration number. With the calculation and consignment it is the idempotency key.",
            "example": "EAD-2026-004411"
          },
          "entryDate": {
            "type": "string",
            "format": "date"
          },
          "consignmentId": {
            "type": "string",
            "description": "A consignment id from the calculation's `consignments`; optional when exactly one consignment was quoted.",
            "example": "c1"
          },
          "actual": {
            "type": "object",
            "required": [
              "duty",
              "importTax",
              "fees",
              "currency"
            ],
            "properties": {
              "duty": {
                "type": "number",
                "minimum": 0
              },
              "importTax": {
                "type": "number",
                "minimum": 0,
                "description": "Actual import VAT or GST."
              },
              "fees": {
                "type": "number",
                "minimum": 0,
                "description": "Actual customs and authority fees."
              },
              "currency": {
                "type": "string",
                "description": "Must equal the calculation's currency. No conversion is performed."
              }
            }
          },
          "lines": {
            "type": "array",
            "maxItems": 100,
            "description": "Optional per-line actual duty; each `lineId` must be a duty line of the consignment.",
            "items": {
              "type": "object",
              "required": [
                "lineId",
                "duty"
              ],
              "properties": {
                "lineId": {
                  "type": "string"
                },
                "duty": {
                  "type": "number",
                  "minimum": 0
                }
              }
            }
          },
          "notes": {
            "type": "string",
            "maxLength": 1000
          }
        }
      },
      "ImportSettlement": {
        "type": "object",
        "description": "A stored settlement: the actuals next to the snapshot of the estimate they are compared with. Facts only: it never changes the calculation, its tax or the entity's tax obligations.",
        "required": [
          "id",
          "calculationId",
          "consignmentId",
          "entryNumber",
          "currency",
          "estimated",
          "actual",
          "variance",
          "withinEstimate",
          "createdAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "example": "cl_impset_8f3a1c9d2b7e4a6f1c0d"
          },
          "calculationId": {
            "type": "string"
          },
          "consignmentId": {
            "type": "string"
          },
          "entryNumber": {
            "type": "string"
          },
          "entryDate": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "currency": {
            "type": "string"
          },
          "destinationTerritory": {
            "type": "string",
            "nullable": true,
            "description": "The consignment's destination customs territory at quote time."
          },
          "precisionAtQuote": {
            "type": "string",
            "nullable": true,
            "description": "The consignment's precision tier when it was quoted."
          },
          "estimated": {
            "type": "object",
            "properties": {
              "duty": {
                "type": "number"
              },
              "importTax": {
                "type": "number"
              },
              "fees": {
                "type": "number"
              },
              "total": {
                "type": "number"
              }
            }
          },
          "actual": {
            "type": "object",
            "properties": {
              "duty": {
                "type": "number"
              },
              "importTax": {
                "type": "number"
              },
              "fees": {
                "type": "number"
              },
              "total": {
                "type": "number"
              }
            }
          },
          "variance": {
            "type": "object",
            "properties": {
              "duty": {
                "type": "object",
                "required": [
                  "amount",
                  "percent"
                ],
                "properties": {
                  "amount": {
                    "type": "number",
                    "description": "actual minus estimated; positive means customs charged more."
                  },
                  "percent": {
                    "type": "number",
                    "nullable": true,
                    "description": "amount divided by the estimate times 100; null when the estimate was 0."
                  }
                }
              },
              "importTax": {
                "type": "object",
                "required": [
                  "amount",
                  "percent"
                ],
                "properties": {
                  "amount": {
                    "type": "number",
                    "description": "actual minus estimated; positive means customs charged more."
                  },
                  "percent": {
                    "type": "number",
                    "nullable": true,
                    "description": "amount divided by the estimate times 100; null when the estimate was 0."
                  }
                }
              },
              "fees": {
                "type": "object",
                "required": [
                  "amount",
                  "percent"
                ],
                "properties": {
                  "amount": {
                    "type": "number",
                    "description": "actual minus estimated; positive means customs charged more."
                  },
                  "percent": {
                    "type": "number",
                    "nullable": true,
                    "description": "amount divided by the estimate times 100; null when the estimate was 0."
                  }
                }
              },
              "total": {
                "type": "object",
                "required": [
                  "amount",
                  "percent"
                ],
                "properties": {
                  "amount": {
                    "type": "number",
                    "description": "actual minus estimated; positive means customs charged more."
                  },
                  "percent": {
                    "type": "number",
                    "nullable": true,
                    "description": "amount divided by the estimate times 100; null when the estimate was 0."
                  }
                }
              }
            }
          },
          "withinEstimate": {
            "type": "boolean",
            "description": "True when the actual total did not exceed the estimated total: the estimate covered the cost and no top-up was owed."
          },
          "lines": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "lineId": {
                  "type": "string"
                },
                "estimatedDuty": {
                  "type": "number"
                },
                "actualDuty": {
                  "type": "number"
                },
                "varianceDuty": {
                  "type": "number"
                },
                "variancePct": {
                  "type": "number",
                  "nullable": true
                }
              }
            }
          },
          "notes": {
            "type": "string",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ImportSettlementResult": {
        "type": "object",
        "required": [
          "settlement",
          "replayed"
        ],
        "properties": {
          "settlement": {
            "$ref": "#/components/schemas/ImportSettlement"
          },
          "replayed": {
            "type": "boolean",
            "description": "True when the same entry was already recorded with the same actuals and the stored settlement is returned."
          }
        }
      },
      "ImportSettlementList": {
        "type": "object",
        "required": [
          "calculationId",
          "settlements",
          "summary"
        ],
        "properties": {
          "calculationId": {
            "type": "string"
          },
          "currency": {
            "type": "string",
            "nullable": true
          },
          "settlements": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ImportSettlement"
            }
          },
          "summary": {
            "type": "object",
            "required": [
              "count",
              "byCurrency"
            ],
            "properties": {
              "count": {
                "type": "integer"
              },
              "byCurrency": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "currency": {
                      "type": "string"
                    },
                    "count": {
                      "type": "integer"
                    },
                    "estimated": {
                      "type": "number"
                    },
                    "actual": {
                      "type": "number"
                    },
                    "variance": {
                      "type": "number"
                    },
                    "variancePercent": {
                      "type": "number",
                      "nullable": true
                    },
                    "withinEstimateCount": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "ImportSettlementVariance": {
        "type": "object",
        "description": "Estimate-versus-entry variance across the entity's settlements, grouped by destination customs territory and precision tier.",
        "required": [
          "filters",
          "groups"
        ],
        "properties": {
          "filters": {
            "type": "object",
            "properties": {
              "from": {
                "type": "string",
                "nullable": true
              },
              "to": {
                "type": "string",
                "nullable": true
              },
              "destination": {
                "type": "string",
                "nullable": true
              },
              "precision": {
                "type": "string",
                "nullable": true
              }
            }
          },
          "groups": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "destinationTerritory": {
                  "type": "string",
                  "nullable": true
                },
                "precision": {
                  "type": "string",
                  "nullable": true
                },
                "count": {
                  "type": "integer"
                },
                "withinEstimateCount": {
                  "type": "integer"
                },
                "meanVariancePercent": {
                  "type": "number",
                  "nullable": true,
                  "description": "Mean of the settlements' total variance percent."
                },
                "medianVariancePercent": {
                  "type": "number",
                  "nullable": true
                },
                "byCurrency": {
                  "type": "array",
                  "description": "Amounts are never added across currencies.",
                  "items": {
                    "type": "object",
                    "properties": {
                      "currency": {
                        "type": "string"
                      },
                      "count": {
                        "type": "integer"
                      },
                      "estimated": {
                        "type": "number"
                      },
                      "actual": {
                        "type": "number"
                      },
                      "variance": {
                        "type": "number"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "CalculationLedgerRow": {
        "type": "object",
        "description": "One tax-calculation request. Facts and hashes only; never the request or response body.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "entityId": {
            "type": [
              "string",
              "null"
            ]
          },
          "apiKeyId": {
            "type": [
              "string",
              "null"
            ]
          },
          "endpoint": {
            "type": "string",
            "enum": [
              "tax_calculate",
              "tax_calculate_partner",
              "csv_import",
              "integration",
              "dashboard"
            ]
          },
          "partnerMerchantId": {
            "type": [
              "string",
              "null"
            ]
          },
          "calculationId": {
            "type": [
              "string",
              "null"
            ],
            "description": "The calculationId returned to the caller; null for a rejected request."
          },
          "transactionRef": {
            "type": [
              "string",
              "null"
            ],
            "description": "The request's merchantRef, when supplied."
          },
          "committed": {
            "type": "boolean"
          },
          "sandbox": {
            "type": "boolean"
          },
          "transactionType": {
            "type": [
              "string",
              "null"
            ]
          },
          "currency": {
            "type": [
              "string",
              "null"
            ]
          },
          "lineCount": {
            "type": [
              "integer",
              "null"
            ]
          },
          "origin": {
            "type": "object",
            "properties": {
              "country": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "region": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "destination": {
            "type": "object",
            "properties": {
              "country": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "region": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "totalAmount": {
            "type": [
              "number",
              "null"
            ]
          },
          "totalTax": {
            "type": [
              "number",
              "null"
            ]
          },
          "inputsHash": {
            "type": [
              "string",
              "null"
            ],
            "description": "Stable hash of the tax-relevant request inputs (references, free text, names and street-level address detail are excluded)."
          },
          "resultHash": {
            "type": [
              "string",
              "null"
            ],
            "description": "Stable hash of the tax-relevant result (treatment, jurisdiction, totals, per-line category, rate and tax)."
          },
          "degraded": {
            "type": "boolean"
          },
          "outcome": {
            "type": "string",
            "enum": [
              "ok",
              "degraded",
              "rejected",
              "error",
              "replayed"
            ]
          },
          "statusCode": {
            "type": "integer"
          },
          "errorCode": {
            "type": [
              "string",
              "null"
            ],
            "description": "Short machine code for a rejected request, never free text."
          },
          "durationMs": {
            "type": [
              "integer",
              "null"
            ]
          },
          "contentVersion": {
            "type": [
              "string",
              "null"
            ],
            "description": "Tax content version the calculation ran on."
          },
          "sourceIpHash": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pseudonymous 6-hex-character bucket of the caller IP, for counting distinct sources; not an identifier."
          },
          "userAgentHash": {
            "type": [
              "string",
              "null"
            ]
          },
          "quote": {
            "type": [
              "object",
              "null"
            ],
            "description": "Committed rows only: the last matching commit=false quote of the same key.",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "match": {
                "type": "string",
                "enum": [
                  "inputs_hash",
                  "transaction_ref"
                ]
              },
              "quotesInWindow": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "differsFromQuote": {
                "type": [
                  "boolean",
                  "null"
                ]
              },
              "inputsChanged": {
                "type": [
                  "boolean",
                  "null"
                ]
              },
              "taxDelta": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "amountDelta": {
                "type": [
                  "number",
                  "null"
                ]
              }
            }
          }
        }
      },
      "TaxCalculateResponse": {
        "type": "object",
        "description": "Result of a tax calculation. Top-level fields carry the overall treatment; each line item is enriched with its own rate and amounts. See TaxCalculateLineResult.",
        "properties": {
          "companyCode": {
            "type": "string",
            "description": "The company code this record was stored under (the casing configured on the entity). Present only when a code resolved or the entity uses company codes; absent otherwise."
          },
          "companyCodeSource": {
            "type": "string",
            "enum": [
              "request",
              "entity_default",
              "none"
            ],
            "description": "Where companyCode came from: the request, or the entity's default ('none' when no code resolved, for example the entity default is outside its validity window, which adds a warning). Present only when a code resolved or the entity uses company codes. When the plan no longer includes company codes but a default is still configured, it keeps applying and a company_codes_entitlement_lapsed warning is added to warnings."
          },
          "calculationId": {
            "type": "string",
            "description": "Clearvo-assigned ID for this calculation (prefix cl_calc_). Use as relatedCalculationId when issuing a credit note.",
            "example": "cl_calc_a1b2c3d4e5f6g7h8i9j0"
          },
          "entityId": {
            "type": "string",
            "description": "The entity (fiscal identity) that processed this calculation."
          },
          "apiVersion": {
            "type": "string"
          },
          "resolvedAt": {
            "type": "string",
            "format": "date-time"
          },
          "sandbox": {
            "type": "boolean",
            "description": "True when calculated against the sandbox environment."
          },
          "committed": {
            "type": "boolean",
            "description": "Echoes the in-scope `commit` (post-default, post-credit-note-negation resolution) this calculation actually ran with — true unless the caller explicitly passed `commit: false`. Lets you confirm what was persisted (or not) without having to remember what you sent, since `commit` now defaults to true when omitted — see TaxCalculateRequest.commit."
          },
          "merchantRef": {
            "type": "string",
            "description": "Echoes the merchantRef from the request, for MoR correlation."
          },
          "paymentMethod": {
            "type": "string",
            "enum": [
              "card",
              "bank_transfer",
              "digital_wallet",
              "cash",
              "other"
            ],
            "description": "Echoes the paymentMethod from the request, if supplied."
          },
          "incoterms": {
            "type": "string",
            "enum": [
              "EXW",
              "FCA",
              "FAS",
              "FOB",
              "CFR",
              "CIF",
              "CPT",
              "CIP",
              "DAP",
              "DPU",
              "DDP"
            ],
            "description": "Echoes the incoterms from the request, if supplied."
          },
          "jurisdiction": {
            "type": "object",
            "description": "The resolved tax jurisdiction for the transaction.",
            "properties": {
              "country": {
                "type": "string",
                "x-clearvo-example-country": "IT",
                "example": "US"
              },
              "region": {
                "type": "string",
                "nullable": true,
                "example": "CA"
              },
              "resolutionMethod": {
                "type": "string",
                "description": "How the jurisdiction was determined: SHIPPING_ADDRESS, BILLING_ADDRESS, IP_GEOLOCATION, BIN_COUNTRY, or VAT_ID."
              },
              "resolutionPrecision": {
                "type": "string",
                "description": "Confidence in the resolved jurisdiction: EXACT, POSTAL, REGION, or COUNTRY."
              },
              "resolutionSource": {
                "type": "string",
                "nullable": true,
                "enum": [
                  "RULE_OVERRIDE"
                ],
                "description": "Present only when a configured rules-engine ADJUSTMENT rule (checkpoint=POST_JURISDICTION_RESOLUTION) rewrote this transaction's resolved country/region after it was first resolved — e.g. a ZIP+4/county correction. Omitted entirely for an address-lookup-only resolution. resolutionMethod/resolutionPrecision are deliberately unchanged by such an override — it corrects WHERE a line is taxed, not WHOSE address that represents."
              },
              "addressPrecision": {
                "type": "string",
                "description": "US only: address tier used for rate lookup — ROOFTOP, ZIP, or STATE. Omitted for non-US."
              },
              "sourcingMethod": {
                "type": "string",
                "enum": [
                  "ORIGIN",
                  "DESTINATION"
                ],
                "description": "US TX/CA/IL only: which address local tax was sourced to. ORIGIN = seller's place of business; DESTINATION = buyer's address. Absent for every other state/country — those always destination-source."
              },
              "sourcingAddress": {
                "type": "string",
                "enum": [
                  "SELLER_PLACE_OF_BUSINESS",
                  "BUYER_ADDRESS"
                ],
                "description": "Companion to sourcingMethod: which address it resolved to."
              },
              "sourcingCitation": {
                "type": "string",
                "description": "Statute/regulation citation backing sourcingMethod, e.g. \"Tex. Tax Code §§ 321.203, 323.203; ...\"."
              },
              "localityOverride": {
                "type": "object",
                "description": "US only: present when a self-administered (\"home-rule\") locality's own rate — not the state-level default — was actually applied (Colorado's home-rule cities, e.g. Boulder, are the seeded case). Omitted entirely (not null) whenever no locality-scoped row won the lookup.",
                "properties": {
                  "locality": {
                    "type": "string",
                    "description": "Clearvo-controlled locality code, e.g. 'BOULDER'."
                  },
                  "region": {
                    "type": "string",
                    "description": "US state the locality sits in, e.g. 'CO'."
                  },
                  "taxAuthorityId": {
                    "type": "number",
                    "nullable": true,
                    "description": "CCH's stable tax_authority_id for the matched city-level authority, if resolved."
                  },
                  "reason": {
                    "type": "string",
                    "description": "Statute/ordinance citation backing the override."
                  }
                }
              },
              "conflicts": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "field": {
                      "type": "string"
                    },
                    "values": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "supplier": {
            "allOf": [
              {
                "$ref": "#/components/schemas/TaxCalcParty"
              }
            ],
            "description": "The supplier party for this calculation. On a 'sale' this is your own entity (isEntity: true, no validation fields). On a 'purchase' this is the counterparty — the vendor (isEntity: false, with b2bOverride/taxIdValidated/taxIdValidationStatus). See entityRole."
          },
          "customer": {
            "allOf": [
              {
                "$ref": "#/components/schemas/TaxCalcParty"
              }
            ],
            "description": "The customer party for this calculation. On a 'sale' this is the counterparty — the buyer (isEntity: false, with b2bOverride/taxIdValidated/taxIdValidationStatus). On a 'purchase' this is your own entity (isEntity: true, no validation fields). See entityRole."
          },
          "entityRole": {
            "type": "string",
            "enum": [
              "supplier",
              "customer"
            ],
            "description": "Which of the two party blocks above is your own entity — 'supplier' for a 'sale', 'customer' for a 'purchase'. The other block is the counterparty."
          },
          "selfAssessedTaxAmount": {
            "type": "number",
            "description": "US consumer's use tax only (present when a line was computed as use tax): the sum of the lines' useTax.selfAssessedTaxAmount."
          },
          "selfAssessedKind": {
            "type": "string",
            "enum": ["REVERSE_CHARGE", "IMPORT_VAT_POSTPONED", "USE_TAX"],
            "description": "Present with selfAssessedTaxAmount; USE_TAX for a use tax calculation."
          },
          "shipFromCountry": {
            "type": "string",
            "description": "The dispatch country the calculation used (ISO 3166-1 alpha-2): `shipFrom.country` when you sent it, else your entity's own country (sales only)."
          },
          "shipFromSource": {
            "type": "string",
            "enum": [
              "request",
              "businessProfile"
            ],
            "description": "`request` when you sent `shipFrom.country`; `businessProfile` when it was taken from your entity's own country because `shipFrom` was omitted. Not a warning: the fallback adds nothing to `warnings`."
          },
          "shipFromRegion": {
            "type": "string",
            "description": "The state or region of the dispatch address the calculation used (a US state code such as `TX`). Present when known: the `shipFrom.region` you sent or, for a US dispatch with no state, your entity's own state. Texas, California and Illinois source some in-state sales to the seller's own location, so this decides the rate there."
          },
          "shipFromPostalCode": {
            "type": "string",
            "description": "The postal code of the dispatch address the calculation used. Present when known: the `shipFrom.postalCode` you sent or, for a US dispatch with no state, your entity's own ZIP code."
          },
          "shipFrom": {
            "allOf": [{ "$ref": "#/components/schemas/TaxAddress" }],
            "description": "The full dispatch address the calculation used: `country` always, plus whichever of `line1`, `line2`, `city`, `region` and `postalCode` are known. The same information as `shipFromCountry`, `shipFromRegion` and `shipFromPostalCode`, as one address object."
          },
          "sellerRegistration": {
            "type": "object",
            "description": "The seller's registration status in the resolved jurisdiction, which governs whether tax is collected. On a purchase this is your own entity's registration status in the destination country, not the vendor's.",
            "properties": {
              "status": {
                "type": "string",
                "description": "e.g. REGISTERED, PENDING, NOT_REGISTERED."
              },
              "registrationNumber": {
                "type": "string"
              },
              "canCollectTax": {
                "type": "boolean"
              },
              "pricingModel": {
                "type": "string",
                "description": "e.g. TAX_EXCLUSIVE or TAX_INCLUSIVE."
              },
              "invoiceNote": {
                "type": "string"
              },
              "reason": {
                "type": "string",
                "description": "Explains why canCollectTax is false despite a registration being on file."
              },
              "reasonCode": {
                "type": "string",
                "description": "Machine-readable counterpart to `reason`."
              },
              "scheme": {
                "type": "string",
                "description": "Coverage provenance — names the registration/program that actually resolved this result (e.g. 'CO_SUTS' for a sale covered by Colorado's consolidated filing program) rather than leaving the caller to infer it from registrationNumber alone. Omitted (not null) whenever no scheme-bearing row was matched."
              },
              "schemeLabel": {
                "type": "string",
                "description": "Human-readable display label for `scheme`, when present."
              }
            }
          },
          "ioss": {
            "type": "object",
            "description": "Present when IOSS treatment is applied.",
            "properties": {
              "number": {
                "type": "string"
              },
              "registrationCountry": {
                "type": "string"
              },
              "totalGoodsValue": {
                "type": "number",
                "description": "Physical-goods-only subtotal (net of line discounts) in the order currency. This is NOT the converted figure; see thresholdTest.convertedValue."
              },
              "currency": {
                "type": "string",
                "description": "The order currency."
              },
              "thresholdTest": {
                "type": "object",
                "description": "How the EUR 150 test was done. The order-currency value is converted to EUR with a static ECB reference-rate table (a periodically refreshed snapshot, not a live rate) before comparing. A currency missing from the table is never treated as IOSS-eligible; the response then carries degraded: true with a degradedReason naming the currency.",
                "properties": {
                  "thresholdValue": {
                    "type": "number",
                    "example": 150
                  },
                  "thresholdCurrency": {
                    "type": "string",
                    "example": "EUR"
                  },
                  "orderCurrency": {
                    "type": "string"
                  },
                  "orderValue": {
                    "type": "number",
                    "description": "Absolute consignment value in the order currency."
                  },
                  "convertedValue": {
                    "type": "number",
                    "description": "orderValue converted to thresholdCurrency."
                  },
                  "fxRate": {
                    "type": "number",
                    "description": "Multiplier from the order currency to thresholdCurrency."
                  },
                  "fxRateSnapshotDate": {
                    "type": "string",
                    "description": "Date of the static rate table used."
                  }
                }
              }
            }
          },
          "duties": {
            "$ref": "#/components/schemas/DutiesBlock",
            "description": "Present when duties were requested (duties.include, or the dutiesEnabled setting), or as status 'disabled' when the cart carries duty inputs while duties are off. Never changes any tax figure."
          },
          "consignments": {
            "type": "array",
            "description": "One entry per customs border crossing in the cart. Estimates, never tax. Present only when duties were requested and the status is not 'disabled'.",
            "items": {
              "$ref": "#/components/schemas/DutyConsignment"
            }
          },
          "customsDuty": {
            "type": "object",
            "description": "Present only when a SELLER_COST fee_rules row applied to this calculation — today, only the EU's temporary (2026-07-01 to 2028-06-30) per-distinct-tariff-classification customs duty on low-value IOSS consignments. Sibling of `ioss` above, never nested inside it, and never an entry in `summary.retailDeliveryFees[]`. This is an informational ESTIMATE of a cost borne by the declarant (the IOSS holder or their indirect customs representative) — it is NOT VAT, it is NOT collected from the buyer, and it is NEVER included in totalTax/totalAmountWithTax (includedInTotals is always false). Not refunded when goods are returned. If the merchant chooses to recharge this amount to the buyer at the time of sale, that recharge becomes part of the VAT-taxable amount of the sale itself — the recharge is VAT-able, this field is not. When duties are also requested, the same EUR 3 appears in the consignment's `duty` (source fee_rule) as the IOSS holder's cost: `customsDuty` is that one fee, `consignments[]` is the whole landed-cost estimate. Never add the two together.",
            "properties": {
              "feeCode": {
                "type": "string",
                "description": "The fee_rules row's own fee_code.",
                "example": "EU_LOW_VALUE_CONSIGNMENT_CUSTOMS_DUTY"
              },
              "currency": {
                "type": "string",
                "description": "Always 'EUR' — the duty is never converted into the calculation's own currency; no FX is involved.",
                "example": "EUR"
              },
              "amount": {
                "type": "number",
                "description": "Total duty owed this calculation — perItemAmount × itemCount.",
                "example": 6
              },
              "perItemAmount": {
                "type": "number",
                "description": "The per-distinct-classification rate the rule charges.",
                "example": 3
              },
              "itemCount": {
                "type": "integer",
                "description": "Distinct qualifying classification groups counted this calculation — see items[] below.",
                "example": 2
              },
              "classificationDigits": {
                "type": "integer",
                "description": "The tariff-code digit-length used to group lines into items[] (e.g. 6 = HS6/H7).",
                "example": 6
              },
              "items": {
                "type": "array",
                "description": "One entry per distinct normalised classification code (ascending), plus one entry per missing/malformed-code line (classificationCode: null, sorted last).",
                "items": {
                  "type": "object",
                  "properties": {
                    "classificationCode": {
                      "type": "string",
                      "nullable": true,
                      "example": "610419"
                    },
                    "lineIds": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "linesMissingCommodityCode": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "lineIds of every qualifying line whose commodityCode was missing or too malformed/short to classify — each also appears as its own null-classificationCode entry in items[] above. Absent when every qualifying line had a usable code."
              },
              "includedInTotals": {
                "type": "boolean",
                "const": false,
                "description": "Always false — this amount is never folded into totalTax/totalAmountWithTax."
              },
              "payableBy": {
                "type": "string",
                "enum": [
                  "DECLARANT"
                ],
                "description": "Always 'DECLARANT' — owed by the IOSS holder or their indirect customs representative, never the buyer."
              },
              "estimate": {
                "type": "boolean",
                "const": true,
                "description": "Always true — customs assesses the actual debt on release; this is a forecast, not a final figure."
              },
              "effectiveFrom": {
                "type": "string",
                "format": "date",
                "description": "The resolved fee_rules row's own effective_from.",
                "example": "2026-07-01"
              }
            }
          },
          "customsDutyNote": {
            "type": "string",
            "enum": [
              "NOT_REVERSED_ON_CREDIT_NOTE"
            ],
            "description": "Present only on a credit note whose original invoice (same cart shape) would otherwise have owed a customsDuty — the duty accrued on the original import declaration is never refunded when goods are returned, so the credit note neither charges nor reverses it. `customsDuty` itself is omitted (not zeroed) on the same response."
          },
          "taxTreatment": {
            "type": "string",
            "enum": [
              "STANDARD",
              "REVERSE_CHARGE",
              "ZERO_RATED",
              "EXEMPT",
              "OUT_OF_SCOPE",
              "SELLER_NOT_REGISTERED",
              "IOSS",
              "SIMPLIFIED",
              "UNSUPPORTED_JURISDICTION",
              "RATE_UNAVAILABLE"
            ],
            "description": "The tax treatment applied to the transaction. UNSUPPORTED_JURISDICTION (taxCode O, 0 tax) means the destination is a well-formed country the engine does not calculate tax for; see unsupportedJurisdiction. RATE_UNAVAILABLE (taxCode O, 0 tax) means a supported non-US country or region has no tax rate on file for the calculation date: the response is also degraded true with degradedReason RATE_NOT_FOUND, a rate_not_found warning and line outcome reasonCode rate_not_found. It is not a real zero-rated sale; do not rely on the amount."
          },
          "unsupportedJurisdiction": {
            "type": "object",
            "required": [
              "country",
              "party"
            ],
            "description": "Present only when a line resolved to a country we do not calculate tax for. Test for its presence: 0 tax was returned because the country is not supported, not because no tax is due. Absent for every supported country.",
            "properties": {
              "country": {
                "type": "string",
                "description": "The unsupported country (ISO 3166-1 alpha-2)."
              },
              "party": {
                "type": "string",
                "enum": [
                  "customer",
                  "seller"
                ],
                "description": "Whose country it is: seller when the place of supply was the seller's own address, else customer."
              }
            }
          },
          "taxCode": {
            "type": "string",
            "description": "EN16931 tax code applied to the transaction. There is no `taxCode` field on POST /v1/send — that category is always a resolved output there too. Use `clientTaxCode` (this value echoed back as a matching client tax code, when one exists for this entity — see the Client Tax Codes endpoints) or a `taxTreatment` hint on the invoice line instead.",
            "x-clearvo-enum-docs": {
              "S": "Standard rate",
              "AA": "Reduced rate",
              "AE": "Reverse charge",
              "E": "Exempt",
              "K": "Intra-community supply (zero-rated)",
              "G": "Export (zero-rated)",
              "Z": "Zero rate",
              "O": "Out of scope"
            }
          },
          "transactionType": {
            "type": "string",
            "enum": [
              "invoice",
              "credit_note"
            ],
            "description": "Echoes the request transactionType (defaults to invoice)."
          },
          "relatedCalculationId": {
            "type": "string",
            "description": "Present when this record is a credit note — the ID of the invoice it reverses."
          },
          "refundedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Present when this invoice has been marked refunded via POST /v1/tax/calculate/{id}/refund."
          },
          "totalTax": {
            "type": "number",
            "description": "Top-level convenience alias for summary.totalTax. Never includes customsDuty.amount — the EU per-item customs duty estimate (when present) is not included in this total; it is a separate, informational estimate, not tax.",
            "example": 8.75
          },
          "summary": {
            "type": "object",
            "description": "Transaction-level totals.",
            "properties": {
              "totalAmount": {
                "type": "number",
                "description": "Sum of taxableAmount across line items (tax-exclusive).",
                "example": 100.0
              },
              "totalDiscount": {
                "type": "number",
                "description": "Sum of discounts applied across line items."
              },
              "totalTax": {
                "type": "number",
                "description": "Sum of taxAmount across line items, plus any state retail delivery fees. The EU per-item customs duty estimate (customsDuty.amount, when present) is NOT included in this total — it is a separate, informational estimate of a cost borne by the IOSS holder/customs representative, not tax.",
                "example": 8.75
              },
              "totalAmountWithTax": {
                "type": "number",
                "description": "Sum of totalAmount across line items, plus any state retail delivery fees. The EU per-item customs duty estimate (customsDuty.amount, when present) is NOT included in this total — see customsDuty's own description for why.",
                "example": 108.75
              },
              "totalDuty": {
                "type": "number",
                "description": "Estimated import duty across the quoted consignments. Present only when duties were quoted. Never included in totalTax or totalAmountWithTax."
              },
              "totalImportTax": {
                "type": "number",
                "description": "Estimated import VAT/GST across the quoted consignments (0 where the tax is already collected at checkout, as under IOSS)."
              },
              "totalImportFees": {
                "type": "number",
                "description": "Estimated customs-authority fees across the quoted consignments."
              },
              "totalLandedCost": {
                "type": "number",
                "description": "goods + freight + insurance + duty + import tax + fees across the quoted consignments. A merchant pricing figure, never a charge."
              },
              "sellerBorneImportCosts": {
                "type": "number",
                "description": "Duty, import tax and fees borne by the seller on seller-responsible consignments; never part of any amount due."
              },
              "importChargesAtCheckout": {
                "type": "number",
                "description": "Present only when a consignment is collectedAtCheckout. The one duty amount a checkout may add: show it as its own line, \"Estimated import charges\", never as tax."
              },
              "totalAmountDue": {
                "type": "number",
                "description": "Present only alongside importChargesAtCheckout: totalAmountWithTax + importChargesAtCheckout. Charge this when present."
              },
              "retailDeliveryFees": {
                "type": "array",
                "description": "State buyer fees owed on top of sales tax: retail delivery fees (flat per delivery when a delivery into the state contains taxable tangible goods) and, for California buyers, the Covered Electronic Waste Recycling Fee on screens over 4 inches (per unit, by lineItems[].screenSizeInches). Not taxed. Included in totalTax and totalAmountWithTax; lineItems[].taxAmount stays sales tax only. Present only when at least one fee applies. The field name predates non-delivery fees.",
                "items": {
                  "type": "object",
                  "properties": {
                    "state": {
                      "type": "string",
                      "description": "Two-letter state code the fee is owed to.",
                      "example": "CO"
                    },
                    "name": {
                      "type": "string",
                      "description": "Statutory fee name, suitable for invoice display.",
                      "example": "Colorado Retail Delivery Fee"
                    },
                    "amount": {
                      "type": "number",
                      "description": "Fee amount owed (per delivery for delivery fees; per unit multiplied by quantity for the California Covered Electronic Waste Recycling Fee).",
                      "example": 0.31
                    },
                    "appliedDefault": {
                      "type": "boolean",
                      "enum": [
                        true
                      ],
                      "description": "Present (always true) only when a documented safe default was applied because an input was missing, e.g. screenSizeInches not supplied so the lowest tier was applied."
                    },
                    "defaultReason": {
                      "type": "string",
                      "description": "Short reason accompanying appliedDefault.",
                      "example": "screenSizeInches not supplied; lowest tier applied"
                    },
                    "defaultedLineIds": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Ids of the request lines the default applied to."
                    }
                  }
                }
              }
            }
          },
          "lineItems": {
            "type": "array",
            "description": "Per-line results.",
            "items": {
              "$ref": "#/components/schemas/TaxCalculateLineResult"
            }
          },
          "lineResults": {
            "type": "array",
            "description": "Alias for lineItems — same data, both fields are present.",
            "items": {
              "$ref": "#/components/schemas/TaxCalculateLineResult"
            }
          },
          "jurisdictionBreakdown": {
            "type": "array",
            "description": "Per-authority rate breakdown (US state/county/city split, when applicable).",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "level": {
                  "type": "string"
                },
                "rate": {
                  "type": "number"
                },
                "taxableAmount": {
                  "type": "number"
                },
                "taxAmount": {
                  "type": "number"
                }
              }
            }
          },
          "degraded": {
            "type": "boolean",
            "description": "True when the engine could not resolve a rate and fell back to rate=0. Never throws a 500."
          },
          "degradedReason": {
            "type": "string"
          },
          "staleCacheReason": {
            "type": "string",
            "description": "Present only when `degraded` was caused by a CCH outage papered over with a static backup rate rather than left at 0% — machine-readable so you can distinguish this specific (rate-substituted) degraded cause from every other one, e.g. 'cch_unavailable_using_state_fallback'."
          },
          "pendingCertificates": {
            "type": "array",
            "description": "Present when an inline `lineItems[].exempt: true` claim (with no matching active ECM certificate) created new PENDING_CERTIFICATE exemption-certificate records on this call.",
            "items": {
              "type": "object",
              "properties": {
                "certId": {
                  "type": "string"
                },
                "certRef": {
                  "type": "string"
                },
                "ecmUrl": {
                  "type": "string",
                  "description": "Link to review/upload the actual certificate in the Clearvo dashboard."
                },
                "certificateType": {
                  "type": "string",
                  "enum": [
                    "RESALE",
                    "MANUFACTURING",
                    "AGRICULTURAL",
                    "ENERGY",
                    "EXEMPT_ORG",
                    "GOVERNMENT",
                    "DIRECT_PAY",
                    "BLANKET_OTHER"
                  ],
                  "description": "The reason recorded on this pending record — that line's own lineItems[].exemptionReason, or BLANKET_OTHER if it omitted one."
                }
              }
            }
          },
          "taxReporting": {
            "type": "object",
            "description": "Present when a taxReportingCurrency was requested and FX conversion applied.",
            "properties": {
              "currency": {
                "type": "string"
              },
              "taxTotal": {
                "type": "number"
              },
              "total": {
                "type": "number"
              },
              "fxRate": {
                "type": "number"
              },
              "fxRateAsOf": {
                "type": "string"
              }
            }
          },
          "ruleLog": {
            "type": "array",
            "description": "Every rule that fired on this calculation, in firing order, scope-reduced for the public API — a Global/Tenant (platform) row never carries its internal ruleId/code, only an Entity/Organisation row (your own rule) keeps ruleCode. Absent entirely (never an empty array) when no rule fired.",
            "items": {
              "type": "object",
              "properties": {
                "ruleKind": {
                  "type": "string",
                  "enum": [
                    "NORMALIZATION",
                    "ENRICHMENT",
                    "ADJUSTMENT",
                    "WARNING",
                    "ERROR"
                  ]
                },
                "label": {
                  "type": "string"
                },
                "ruleCode": {
                  "type": "string",
                  "description": "Present only for an Entity-/Organisation-scoped rule (your own)."
                },
                "property": {
                  "type": "string",
                  "nullable": true
                },
                "beforeValue": {},
                "afterValue": {}
              }
            }
          },
          "ruleLogTruncated": {
            "type": "boolean",
            "enum": [
              true
            ],
            "description": "Present only (true) when the full rule-log entry count exceeded the public-response cap (100). Call GET /rules-engine/trace for the complete, uncapped trace."
          },
          "effectiveInput": {
            "type": "object",
            "nullable": true,
            "description": "The actual header facts (post pre-calc-rules-pass) the engine resolved jurisdiction/rate/classification against — never your raw, pre-rules request.",
            "properties": {
              "country": {
                "type": "string",
                "nullable": true
              },
              "customerCountry": {
                "type": "string",
                "nullable": true
              },
              "supplierCountry": {
                "type": "string",
                "nullable": true
              },
              "customerType": {
                "type": "string",
                "nullable": true
              },
              "currency": {
                "type": "string",
                "nullable": true
              }
            }
          },
          "warnings": {
            "type": "array",
            "description": "Every matched WARNING-kind rules-engine rule (pre- or post-calculation), plus platform warnings. Absent entirely (never an empty array) when none fired. A `country_not_supported` entry (scopeLevel PLATFORM, with country, party, message and a hint pointing at GET /v1/tax/jurisdictions) accompanies an unsupportedJurisdiction result. The EU distance-sales entries have scopeLevel `PLATFORM`: `approaching_distance_sales_threshold` (recorded EU consumer sales have reached 80% of the limit) and `distance_sales_threshold_crossed`. Each carries a `message` stating that the decision rests on sales recorded in Clearvo; sales made on other channels are not visible to it.",
            "items": {
              "type": "object",
              "properties": {
                "ruleCode": {
                  "type": "string"
                },
                "scopeLevel": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                },
                "country": {
                  "type": "string",
                  "description": "country_not_supported only: the unsupported country."
                },
                "party": {
                  "type": "string",
                  "enum": [
                    "customer",
                    "seller"
                  ],
                  "description": "country_not_supported only."
                },
                "hint": {
                  "type": "string",
                  "description": "country_not_supported only: points to GET /v1/tax/jurisdictions."
                }
              }
            }
          },
          "externalOriginalReference": {
            "type": "string",
            "description": "Echo of the request's externalOriginalReference — present on a credit note whose original invoice Clearvo has no record of."
          },
          "relatedInvoiceSource": {
            "type": "string",
            "enum": [
              "clearvo",
              "external"
            ],
            "description": "On a credit note: 'clearvo' when it reverses a Clearvo calculation (relatedCalculationId), 'external' when it references an invoice issued elsewhere (externalOriginalReference)."
          },
          "marketplaceFacilitator": {
            "type": "object",
            "description": "Present only on a calculation flagged isMarketplaceFacilitatedSale: true. marketplaceFacilitatorTax is the sum of the per-line memos — tax the facilitator collects, NOT included in summary.totalTax or any total. feesSkipped names each fee that was not applied because the facilitator, not the seller, is the retailer of the facilitated lines.",
            "properties": {
              "linesRelieved": {
                "type": "integer"
              },
              "linesSellerCollects": {
                "type": "integer"
              },
              "marketplaceFacilitatorTax": {
                "type": "number"
              },
              "feesSkipped": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "feeCode": {
                      "type": "string"
                    },
                    "region": {
                      "type": "string",
                      "nullable": true
                    },
                    "name": {
                      "type": "string"
                    },
                    "reasonCode": {
                      "type": "string",
                      "enum": [
                        "MARKETPLACE_FACILITATOR_FEE_NOT_SELLERS"
                      ]
                    }
                  }
                }
              }
            }
          }
        }
      },
      "TaxCalculateLineResult": {
        "type": "object",
        "description": "A single calculated line item.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Your line item ID, echoed from the request."
          },
          "taxCategory": {
            "type": "string",
            "description": "Resolved product category slug (e.g. digital_services)."
          },
          "taxCode": {
            "type": "string",
            "description": "EN16931 tax code for this line."
          },
          "rate": {
            "type": "number",
            "description": "Effective tax rate as a decimal fraction (0.19 = 19%, 0 = zero-rated).",
            "example": 0.19
          },
          "duty": {
            "$ref": "#/components/schemas/LineDuty",
            "description": "Estimated import duty for this line. Present only when duties were quoted for it. Never included in this line's taxAmount."
          },
          "clientTaxCode": {
            "type": "string",
            "description": "Your own ERP tax code for this line — OUTPUT only, never an input on this endpoint (send it on POST /v1/send instead). The engine calculates from raw transaction data as normal, then looks the client-codes table up in reverse (by the resolved Tax Decision) and returns the caller's own matching code, for direction 'sale'-or-unset codes only. Omitted when no eligible code matches, or more than one does (an intentionally-allowed but ambiguous duplicate).",
            "example": "A1"
          },
          "recoverability": {
            "type": "object",
            "description": "Input-tax recoverable/blocked split for this line. Present only when this calculation's transactionDirection is 'purchase'; entirely absent for every 'sale' line.",
            "properties": {
              "recoverablePercent": {
                "type": "number",
                "nullable": true,
                "description": "0–100 (not a 0–1 fraction). null only when status is RECOVERABILITY_NOT_CONFIGURED."
              },
              "recoverableAmount": {
                "type": "number",
                "nullable": true,
                "description": "Decimal units. null only when status is RECOVERABILITY_NOT_CONFIGURED."
              },
              "blockedAmount": {
                "type": "number",
                "nullable": true,
                "description": "Decimal units, taxAmount - recoverableAmount exactly. null only when status is RECOVERABILITY_NOT_CONFIGURED."
              },
              "recoverabilityRate": {
                "type": "number",
                "nullable": true,
                "description": "recoverablePercent / 100 — a 0–1 fraction, mirroring this line's own `rate` convention."
              },
              "allocationSource": {
                "type": "string",
                "enum": [
                  "CLIENT_TAX_CODE",
                  "DEFAULT_CATALOG_CODE",
                  "MANUAL_OVERRIDE",
                  "AI_ALLOCATED",
                  "NOT_CONFIGURED",
                  "US_USE_TAX_NOT_RECOVERABLE"
                ],
                "description": "CLIENT_TAX_CODE — an entity-configured client tax code. MANUAL_OVERRIDE — recoverablePercentOverride was supplied. NOT_CONFIGURED — no override and either no client tax code matched this line, or one matched with no recoverabilityType. DEFAULT_CATALOG_CODE and AI_ALLOCATED remain valid enum values but can no longer occur — they named a global default-catalog fallback and an AI-assisted purchase allocator, both removed 2026-09-01; resolution is client-configured client tax codes only now."
              },
              "status": {
                "type": "string",
                "enum": [
                  "RESOLVED",
                  "RECOVERABILITY_NOT_CONFIGURED"
                ]
              },
              "needsReview": {
                "type": "boolean",
                "description": "REMOVED-BUT-PRESERVED, 2026-09-01: was true when the now-removed AI-assisted purchase allocator sourced this line's recoverability as AI_ALLOCATED and gated it for human review. allocationSource can no longer be AI_ALLOCATED, so this field can no longer be set — kept in the schema only because a shipped field is never removed."
              },
              "selfAssessedOutputTax": {
                "type": "number",
                "description": "Present only for a reverse-charge purchase line — a cross-border service purchase (EU VAT Directive Arts. 44/196), an intra-community acquisition of goods (Arts. 20-23), or a cross-border import of goods (Art. 30) whose destination's Postponed VAT Accounting resolves applied: true (see sourcingRationale.pvaAvailability) — the output VAT this entity self-assessed on the purchase, equal to this line's own taxAmount, reported as a distinct amount alongside (never netted into) recoverableAmount/blockedAmount above. A fully-recoverable line nets to zero cash VAT without the two reported fields themselves ever being combined. Omitted for a domestically-invoiced purchase line, or a border-paid (non-postponed) import line."
              }
            }
          },
          "useTax": {
            "type": "object",
            "description": "US consumer's use tax. Present only on a 'purchase' line delivered to a US state enabled for use tax; absent on every other line. The line's taxAmount is the use tax due at the use location; this object says what the vendor charged, what is credited and what the buyer self-assesses. A state with no local use tax on general purchases (Illinois, Iowa) is state level only; a state that collects local use tax (Nebraska, Minnesota, Texas) includes the local components.",
            "required": ["useLocation", "taxDueAtUseLocation", "creditAllowed", "selfAssessedTaxAmount", "toleranceWaived", "acceptedShortfall", "breakdown", "assumptions", "reviewRequired"],
            "properties": {
              "useLocation": {
                "type": "object",
                "properties": {
                  "country": { "type": "string", "enum": ["US"] },
                  "state": { "type": "string" },
                  "county": { "type": "string", "nullable": true },
                  "city": { "type": "string", "nullable": true },
                  "postalCode": { "type": "string", "nullable": true },
                  "precision": { "type": "string", "enum": ["ROOFTOP", "ZIP", "STATE"] },
                  "basis": { "type": "string", "enum": ["LINE_SHIP_TO", "HEADER_SHIP_TO", "ENTITY_ADDRESS"], "description": "Which address decided the use location. ENTITY_ADDRESS: no ship-to was sent and your entity's own address was assumed (also listed in assumptions)." }
                }
              },
              "taxDueAtUseLocation": { "type": "number", "description": "Use tax at the use location before credit. Equals the line's taxAmount." },
              "taxChargedByVendor": { "type": "number", "nullable": true, "description": "The line's statedTaxAmount; null when none was sent (on an invoice the vendor is then assumed to have charged 0, assumption VENDOR_TAX_NOT_PROVIDED_ASSUMED_ZERO; on a purchase order no assumption is made)." },
              "taxExpectedFromVendor": { "type": "number", "nullable": true, "description": "What the vendor should have charged at the use location (the sales tax there); null when it could not be determined." },
              "creditAllowed": { "type": "number", "description": "Vendor tax credited against the use tax due (tax charged for the same state only). 0 when the charge matches another state's rate or is unexplained." },
              "selfAssessedTaxAmount": { "type": "number", "description": "What the buyer self-assesses and accrues. taxDueAtUseLocation = creditAllowed + selfAssessedTaxAmount + toleranceWaived + acceptedShortfall." },
              "acceptedShortfall": { "type": "number", "description": "Use tax above what the vendor charged that is not self-assessed because the vendor's charge is accepted (an in-state vendor's origin-based charge, a remote seller's flat local rate); 0 otherwise." },
              "toleranceWaived": { "type": "number", "description": "Use tax a use-tax tolerance rule (template us_use_tax_tolerance) waived on this line; 0 unless such a rule applied. By default nothing is waived." },
              "breakdown": {
                "type": "array",
                "description": "Per taxing authority, state first. Sums to the line figures; a credit is applied state first (in the state's own order where it has one).",
                "items": {
                  "type": "object",
                  "properties": {
                    "level": { "type": "string" },
                    "name": { "type": "string" },
                    "taxType": { "type": "string", "description": "01 state, 02 county, 03 county district, 04 city, 05 city district." },
                    "rate": { "type": "number" },
                    "taxDue": { "type": "number" },
                    "creditApplied": { "type": "number" },
                    "selfAssessed": { "type": "number" }
                  }
                }
              },
              "assumptions": { "type": "array", "items": { "type": "string" }, "description": "Closed list of machine-readable assumptions behind this line, for example USE_LOCATION_ASSUMED_ENTITY_ADDRESS, VENDOR_TAX_NOT_PROVIDED_ASSUMED_ZERO, STATE_ONLY_LOCAL_USE_TAX_NOT_COMPUTED, OTHER_STATE_CREDIT_NOT_VERIFIED." },
              "vendorChargeStory": {
                "type": "string",
                "nullable": true,
                "enum": ["MATCHES_DESTINATION", "MATCHES_ORIGIN_SOURCING", "MATCHES_SIMPLIFIED_REMOTE_SELLER_RATE", "SAME_STATE_PARTIAL", "OTHER_STATE_RATE", "STATE_ONLY_OR_NONE", "UNEXPLAINED", "NOT_PROVIDED"],
                "description": "What the vendor most plausibly charged, inferred from the stated tax: the destination rate, an in-state vendor's own location rate, a Texas remote seller's flat local rate, a missed local tax, another state's rate, state only or none, or unexplained. null when no use tax is due."
              },
              "reviewRequired": { "type": "boolean", "description": "True when a person should look at this line (another state's rate, an unexplained charge, a data gap). Never changes the arithmetic." },
              "reviewReasons": { "type": "array", "items": { "type": "string" } }
            }
          },
          "taxVerification": {
            "type": "object",
            "description": "Vendor-charged tax verification for this line. Present only when transactionDirection is 'purchase' AND the request supplied lineItems[].statedTaxAmount for this line AND this is not a border-paid (non-postponed) cross-border import-of-goods line (movement: 'import'); otherwise entirely absent — including every 'sale' line, a purchase line with no statedTaxAmount, and a genuine border-paid import line (EU/UK import VAT is assessed on customs value + duty + freight, never this line's invoice-net amount, which is the only base this API has — a Postponed-VAT-Accounting import line, self-assessed at 0, IS still verified normally). Never gated on this line's outcome status — NO_TAX_CODE and UNKNOWN_TREATMENT lines verify normally too. Verification is skipped without error in every skipped case, never defaulted to a MATCH.",
            "properties": {
              "statedTaxAmount": {
                "type": "number",
                "description": "Echoed back verbatim from the request."
              },
              "calculatedTaxAmount": {
                "type": "number",
                "description": "Identical to this line's own taxAmount."
              },
              "delta": {
                "type": "number",
                "description": "statedTaxAmount - calculatedTaxAmount, rounded to 2dp. Positive = overcharged, negative = undercharged, (near-)zero = MATCH."
              },
              "verdict": {
                "type": "string",
                "enum": [
                  "MATCH",
                  "OVERCHARGED",
                  "UNDERCHARGED"
                ]
              },
              "recoverableBase": {
                "type": "number",
                "description": "Decimal units — the tax amount used as the base for this line's recoverability split."
              },
              "outcome": {
                "type": "string",
                "nullable": true,
                "enum": [
                  "ACCEPTED_AS_CHARGED",
                  "UNDERCHARGED",
                  "OVERCHARGED",
                  "HELD"
                ],
                "description": "The AP decision layer's own outcome for this line — distinct from (and always computed alongside) verdict above, which stays the plain rounding-tolerance comparison. null only for a genuine border-paid-import line (see reasonCode IMPORT_TAX_PAID_AT_BORDER)."
              },
              "reasonCode": {
                "type": "string",
                "enum": [
                  "WITHIN_TOLERANCE",
                  "TRUSTED_SUPPLIER_OVERRIDE",
                  "OVERCHARGE_EXCEEDS_TOLERANCE",
                  "UNDERCHARGE_EXCEEDS_TOLERANCE",
                  "PO_DISPOSITION_HOLD",
                  "REVERSE_CHARGE_VAT_WRONGLY_CHARGED",
                  "TAX_CHARGED_ON_ZERO_LIABILITY_LINE",
                  "NO_TAX_DUE_NONE_CHARGED",
                  "IMPORT_TAX_PAID_AT_BORDER",
                  "USE_TAX_SELF_ASSESSED",
                  "USE_TAX_SHORTFALL_SELF_ASSESSED",
                  "USE_TAX_VENDOR_CHARGE_REVIEW"
                ],
                "description": "USE_TAX_SELF_ASSESSED: the vendor charged 0 on a line that is taxable at the delivery location, so the buyer self-assesses the full use tax (outcome UNDERCHARGED here means the buyer accrues the tax, never a supplier fault; the ap.decision webhook does not fire for any USE_TAX_* reason). USE_TAX_SHORTFALL_SELF_ASSESSED: the vendor charged less than is due; the charge is credited and the shortfall self-assessed. USE_TAX_VENDOR_CHARGE_REVIEW: nothing more is owed but the vendor's charge matches another state's rate or nothing recognisable; accepted and flagged for review."
              },
              "legalDelta": {
                "type": "number",
                "description": "statedTaxAmount - calculatedTaxAmount, rounded to 2dp — identical to delta, kept as its own field to mirror the AP decision layer's own result shape one-to-one."
              },
              "appliedDelta": {
                "type": "number",
                "description": "0 when outcome is ACCEPTED_AS_CHARGED; equal to legalDelta otherwise — the true remaining reportable exposure."
              },
              "zeroLiabilityBasis": {
                "type": "string",
                "nullable": true,
                "enum": [
                  "REVERSE_CHARGE",
                  "PVA",
                  "EXEMPT",
                  "ZERO_RATED"
                ],
                "description": "Non-null only for a reverse-charge/PVA/exempt/zero-rated line."
              },
              "nonDeductibleStatedTax": {
                "type": "number",
                "description": "The vendor-charged amount that is non-deductible input tax on this line (equal to statedTaxAmount for the two \"vendor wrongly charged tax on a zero-liability line\" reasons, 0 otherwise)."
              },
              "poComparison": {
                "type": "object",
                "description": "The PO<->invoice fact-diff (M.14/RE-6) — present only when this line's poLink resolved to LINKED. A plain fact-diff, never a tax-correctness verdict.",
                "properties": {
                  "exceedsThreshold": {
                    "type": "boolean"
                  },
                  "poTaxCode": {
                    "type": "string",
                    "nullable": true
                  },
                  "poRate": {
                    "type": "number",
                    "nullable": true
                  },
                  "poTaxAmount": {
                    "type": "number",
                    "nullable": true
                  },
                  "invoiceTaxCode": {
                    "type": "string"
                  },
                  "invoiceRate": {
                    "type": "number"
                  },
                  "invoiceTaxAmount": {
                    "type": "number"
                  },
                  "taxAmountDelta": {
                    "type": "number",
                    "nullable": true
                  }
                }
              }
            }
          },
          "taxableBasis": {
            "type": "number",
            "description": "Present (< 1) only when a statute taxes a fixed share of the charge — e.g. 0.8 for Texas information services (Tex. Tax Code §151.351, 20% of the charge exempt). rate is already the effective rate (statutory jurisdiction rate × this basis), so taxAmount = taxableAmount × rate still holds. Omitted when the full charge is taxable.",
            "example": 0.8
          },
          "taxableAmount": {
            "type": "number",
            "description": "Tax-exclusive amount tax is calculated on."
          },
          "discount": {
            "type": "number",
            "description": "This line's own request `discount` (decimal units; 0 when none)."
          },
          "orderDiscountAllocated": {
            "type": "number",
            "description": "This line's allocated share of the request's order-level `orderDiscount` (decimal units; 0 when none), split pro rata to each line's value after its own line discount."
          },
          "totalDiscount": {
            "type": "number",
            "description": "discount + orderDiscountAllocated: everything deducted from this line before tax. taxableAmount = amount − totalDiscount."
          },
          "taxAmount": {
            "type": "number",
            "description": "taxableAmount × rate."
          },
          "totalAmount": {
            "type": "number",
            "description": "taxableAmount + taxAmount."
          },
          "classificationStatus": {
            "type": "string",
            "description": "APPROVED, NEEDS_REVIEW, MANUAL, or PENDING."
          },
          "classificationConfidence": {
            "type": "number",
            "description": "Classification confidence (0-1). 1.0 for EXPLICIT, CODE_MAP, or CACHED sources; lower only for a CACHED_BY_NAME match carried over from its original catalogue classification. No live AI classification ever occurs on this endpoint — see classificationSource."
          },
          "classificationFallback": {
            "type": "boolean",
            "description": "Present and true only when the category was resolved via the default fallback; omitted otherwise."
          },
          "classificationSystem": {
            "type": "string",
            "description": "Present only when classificationSource is CODE_MAP: the system of the code that decided (e.g. stripe, shopify, hs).",
            "example": "shopify"
          },
          "classificationMatchedCode": {
            "type": "string",
            "description": "Present only when classificationSource is CODE_MAP: the code in the map that actually decided. It can be a parent of the code you sent (a Shopify category falls back to its nearest mapped parent; a customs code to its longest mapped prefix).",
            "example": "aa-1-13"
          },
          "classificationSource": {
            "type": "string",
            "nullable": true,
            "enum": [
              "EXPLICIT",
              "CODE_MAP",
              "CACHED",
              "CACHED_BY_NAME",
              "RULE_OVERRIDE",
              "DEFAULT"
            ],
            "description": "HOW this line's tax category was resolved for this call — the provenance signal alongside classificationConfidence/classificationStatus. Always populated when classification data is present on the line, even when taxTreatmentOverride or commodityCode later determined the line's actual rate band, since classification runs unconditionally before jurisdiction/rate-band resolution. No live AI classification ever occurs on this endpoint — EXPLICIT: caller supplied taxCategory directly (confidence 1.0, status APPROVED). CODE_MAP: resolved through the classification code map from one of the codes in classificationCodes (Stripe tax code, Shopify category, customs code, ...) — a maintained deterministic mapping; classificationSystem and classificationMatchedCode say which code decided. CACHED: a prior classification of this exact product (by productCode) was reused, no fresh classification ran. CACHED_BY_NAME: an exact, unambiguous productName match against your product catalogue (no productCode supplied, or it didn't match, but exactly one catalogue entry has this exact name) — a 2+-entry name match is treated as no match rather than guessed. RULE_OVERRIDE: a configured rules-engine ADJUSTMENT rule (checkpoint=POST_CLASSIFICATION) rewrote this line's classified category after it was first resolved — always confidence 1.0, status APPROVED, as authoritative as an explicit caller override. DEFAULT: no code/name match was found (or no product name was supplied at all) — resolves to your configured default category, else the standard physical-goods category."
          },
          "exemption": {
            "type": "object",
            "description": "Present only when a REAL exemption certificate on file was applied to this line item (via customer.exemptionRef/ref). Never present for an inline `lineItems[].exempt: true` claim with no certificate on file — see `exemptionReason` below for that case instead; these fields are never null.",
            "properties": {
              "certificateId": {
                "type": "string"
              },
              "certificateRef": {
                "type": "string"
              },
              "certificateType": {
                "type": "string",
                "enum": [
                  "RESALE",
                  "MANUFACTURING",
                  "AGRICULTURAL",
                  "ENERGY",
                  "EXEMPT_ORG",
                  "GOVERNMENT",
                  "DIRECT_PAY",
                  "BLANKET_OTHER",
                  "DIGITAL_SERVICES_COMMERCIAL_USE",
                  "EXPORT_AUTHORIZATION"
                ]
              },
              "effectiveTo": {
                "type": "string",
                "nullable": true,
                "description": "ISO 8601 date the certificate expires, or null if it has no expiry."
              }
            }
          },
          "exemptionReason": {
            "type": "string",
            "enum": [
              "RESALE",
              "MANUFACTURING",
              "AGRICULTURAL",
              "ENERGY",
              "EXEMPT_ORG",
              "GOVERNMENT",
              "DIRECT_PAY",
              "BLANKET_OTHER"
            ],
            "description": "Present only when this line's exemption came from an inline `lineItems[].exempt: true` claim with NO certificate on file — the request's own lineItems[].exemptionReason, or BLANKET_OTHER if omitted. A plain echo, deliberately separate from `exemption` above: it never implies a certificate record exists, since not every caller uses ECM at all. Mutually exclusive with `exemption` — a real certificate match always takes precedence over an inline claim."
          },
          "jurisdiction": {
            "type": "object",
            "description": "This line's own resolved jurisdiction. A mixed cart (e.g. a physical good alongside a consulting service) can source different lines to different countries — see the sourcing matrix at clearvo.io/docs/tax-calculation#jurisdiction. Optional: absent on the rare engine-error fallback path, which has no per-line jurisdiction data; always present on a normal (non-degraded) calculation.",
            "properties": {
              "country": {
                "type": "string",
                "example": "DE"
              },
              "region": {
                "type": "string",
                "nullable": true,
                "example": "CA"
              },
              "resolutionMethod": {
                "type": "string",
                "enum": [
                  "SHIPPING_ADDRESS",
                  "BILLING_ADDRESS",
                  "IP_GEOLOCATION",
                  "BIN_COUNTRY",
                  "VAT_ID",
                  "SELLER_ADDRESS"
                ],
                "description": "How this line's jurisdiction was determined. SELLER_ADDRESS: the SELLER's own establishment address was the sourcing signal, not the customer's — used for GENERAL_SERVICE lines sold B2C (EU VAT Directive Art. 45 — general, non-digital services are sourced to where the supplier is established, the opposite of DIGITAL_SERVICE's customer-location rule)."
              },
              "resolutionPrecision": {
                "type": "string",
                "enum": [
                  "EXACT",
                  "POSTAL",
                  "REGION",
                  "COUNTRY"
                ],
                "description": "EXACT: all available signals agreed. POSTAL: a special postal-code territory was detected. REGION: a state/province was resolved. COUNTRY: only country-level precision."
              },
              "resolutionSource": {
                "type": "string",
                "nullable": true,
                "enum": [
                  "RULE_OVERRIDE"
                ],
                "description": "Present only when a configured rules-engine ADJUSTMENT rule (checkpoint=POST_JURISDICTION_RESOLUTION) rewrote this line's resolved country/region after it was first resolved — e.g. a ZIP+4/county correction. Omitted entirely for an address-lookup-only resolution. resolutionMethod/resolutionPrecision are deliberately unchanged by such an override — it corrects WHERE a line is taxed, not WHOSE address that represents. `conflicts` below is always omitted when this is present — an authoritative override has already resolved any pre-override ambiguity."
              },
              "conflicts": {
                "type": "array",
                "nullable": true,
                "description": "Present only when this line's location signals disagreed (the EU B2C digital-services 2-of-3 evidence rule).",
                "items": {
                  "type": "object",
                  "properties": {
                    "field": {
                      "type": "string"
                    },
                    "values": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "addressPrecision": {
                "type": "string",
                "nullable": true,
                "enum": [
                  "ROOFTOP",
                  "ZIP",
                  "STATE"
                ],
                "description": "US destinations only — the precision tier used for this line's rate lookup."
              }
            }
          },
          "placeOfSupplyBasis": {
            "type": "string",
            "enum": [
              "ORIGIN_UNDER_THRESHOLD",
              "DESTINATION_THRESHOLD_EXCEEDED",
              "DESTINATION_OSS",
              "DESTINATION_REGISTERED",
              "DESTINATION_DISPATCH_NOT_ESTABLISHMENT",
              "DESTINATION_FAIL_SAFE",
              "DESTINATION_DEFAULT"
            ],
            "description": "Why an EU consumer goods line was taxed in the country it was (Art. 59c of Directive 2006/112/EC). Present only when the EU distance-sales rule was decided for this line. `ORIGIN_UNDER_THRESHOLD`: your home-country VAT, because the goods ship from that country and recorded EU consumer sales are within the limit. `DESTINATION_THRESHOLD_EXCEEDED`: the limit was passed this year or last year. `DESTINATION_OSS` / `DESTINATION_REGISTERED`: you use Union OSS or collect in the destination country. `DESTINATION_DISPATCH_NOT_ESTABLISHMENT`: the goods ship from a country other than the one you are established in (or sales have shipped from elsewhere in the current or preceding year). `DESTINATION_FAIL_SAFE`: an input was missing or unavailable, so destination VAT was charged. `DESTINATION_DEFAULT`: the rule was considered but did not change the outcome. The figures behind the decision are on the stored calculation (dashboard calculation detail and the rules trace), not on this response."
          },
          "placeOfSupplyRule": {
            "type": "string",
            "enum": [
              "GOODS",
              "DIGITAL_SERVICE",
              "GENERAL_SERVICE"
            ],
            "description": "Which place-of-supply regime this line's classified tax category was resolved under — drives both jurisdiction.resolutionMethod and the tax code chosen. GOODS: destination-of-goods sourcing. DIGITAL_SERVICE: customer-location sourcing for both B2B and B2C (EU VAT Directive Art. 58 / Art. 44; the EU's 2-of-3 evidence procedure is Reg 282/2011 Art. 24f). GENERAL_SERVICE: customer-location for B2B (Art. 44), but the SELLER's own country for B2C (Art. 45). See the sourcing matrix at clearvo.io/docs/tax-calculation#jurisdiction for the full table, including geographic scope and the ZA/AE/SA non-EU B2B digital carve-out. A category classified UNSUPPORTED never reaches this field — the whole calculation is rejected with a 422 category_not_supported error first. Optional: mirrors jurisdiction, absent only on the engine-error fallback path."
          },
          "sourcingRationale": {
            "type": "object",
            "description": "Machine-readable explanation of how this line's jurisdiction and treatment were sourced, so you can answer \"why did this line compute this way\" without reverse-engineering it from rate/treatment alone. Optional: absent only on the engine-error fallback path.",
            "properties": {
              "placeOfSupplyRule": {
                "type": "string",
                "enum": [
                  "GOODS",
                  "DIGITAL_SERVICE",
                  "GENERAL_SERVICE"
                ]
              },
              "counterpartyStatusBasis": {
                "type": "string",
                "enum": [
                  "B2B",
                  "B2C"
                ],
                "description": "Whether this line was sourced and taxed under B2B or B2C rules."
              },
              "rateBand": {
                "type": "string",
                "description": "This line's resolved rate band (STANDARD/MIDDLE/REDUCED/SUPER_REDUCED/SPECIAL/ZERO/EXEMPT/etc.). Always present."
              },
              "bandTier": {
                "type": "string",
                "enum": [
                  "OVERRIDE",
                  "COMMODITY_CODE",
                  "EXPLICIT",
                  "COUNTRY",
                  "SCOPE_EU",
                  "SCOPE_US",
                  "SCOPE_GLOBAL",
                  "FALLBACK",
                  "DEGRADED"
                ],
                "description": "Which resolution tier decided rateBand above. OVERRIDE confirms taxTreatmentOverride was honoured; COMMODITY_CODE confirms commodityCode matched a row. Any other value means neither field applied (omitted, or present but not matched) and the band came from the ordinary taxCategory-driven chain instead — the only way to confirm whether an override/commodity code actually took effect."
              },
              "fallbackReason": {
                "type": "string",
                "nullable": true,
                "description": "Present only when counterpartyStatusBasis is the result of a fallback rather than a clean, verified signal — e.g. a taxId that failed live-authority verification (VIES etc.), which resolves as B2C rather than a hard error, or an unverifiable VAT ID resolved via the vatUnverifiableFallback setting. Absent when counterpartyStatusBasis came from a clean VALID/SKIPPED/NOT_CHECKED resolution."
              },
              "shippingAllocationMethod": {
                "type": "string",
                "nullable": true,
                "enum": [
                  "PROPORTIONAL",
                  "ALL_TAXABLE_IF_ANY_TAXABLE",
                  "ALWAYS_EXEMPT",
                  "MAJORITY_VALUE"
                ],
                "description": "Present only on a shipping_handling line whose US destination has a researched cart-aware allocation rule that actually applied — absent for every other line, and absent for a shipping_handling line in a state with no researched rule (which uses the ordinary STANDARD default instead)."
              },
              "flatRateElection": {
                "type": "object",
                "nullable": true,
                "description": "Present only for a US TX group whose matched seller registration carries an active flat-rate election (TX Single Local Use Tax Rate, Tex. Tax Code §151.0595) as of the transaction date — absent for every other line. `appliedProgram` (equal to `programCode`) is present only when the election's flat rate actually governed this line's rate; when a competing rule took precedence instead (TX origin sourcing, for a seller with a qualifying TX place of business), `appliedProgram` is omitted and `note` explains why. AL elections are resolved but not yet applied to a rate — this field is never populated for an AL group today.",
                "properties": {
                  "programCode": {
                    "type": "string",
                    "example": "TX_SLUTR"
                  },
                  "appliedProgram": {
                    "type": "string",
                    "nullable": true,
                    "example": "TX_SLUTR",
                    "description": "Present, and equal to programCode, only when the election's flat rate was actually applied to this line."
                  },
                  "note": {
                    "type": "string",
                    "nullable": true,
                    "description": "Present only when appliedProgram is absent — caller-facing explanation of why an active election was not applied to this line."
                  }
                }
              },
              "incotermsImportLiability": {
                "type": "object",
                "nullable": true,
                "description": "Present only when a caller-supplied incoterms value other than 'DDP' forced this B2C, physical-goods, customs-crossing line to OUT_OF_SCOPE (taxCode 'O') — see the `incoterms` request field's own description. Absent whenever incoterms was omitted, was 'DDP', or the line wasn't a customs-crossing B2C goods shipment in the first place.",
                "properties": {
                  "incoterms": {
                    "type": "string",
                    "example": "DAP"
                  },
                  "shipFromCountry": {
                    "type": "string",
                    "example": "US"
                  },
                  "destinationCountry": {
                    "type": "string",
                    "example": "DE"
                  }
                }
              },
              "importerOfRecord": {
                "type": "object",
                "nullable": true,
                "description": "Present only for a transactionDirection: 'purchase' GOODS line that is a genuine, customs-crossing cross-border purchase (the real supplier's country differs from the resolved destination, and the two are not both in the EU — an EU-EU pair is an intra-community acquisition with no importer-of-record concept and never carries this field). Reports the resolved importer-of-record outcome for THIS line. `value: 'SELLER'` (DDP, from any tier) is why this line computed as an ordinary destination-domestic purchase rather than being rejected or flagged UNKNOWN_TREATMENT; an absent `value` is why this line's outcome.reasonCode is 'missing_importer_of_record' instead.",
                "required": [
                  "source"
                ],
                "properties": {
                  "value": {
                    "type": "string",
                    "enum": [
                      "SELLER",
                      "BUYER"
                    ],
                    "description": "Omitted (not null) when every resolution tier came up empty."
                  },
                  "source": {
                    "type": "string",
                    "enum": [
                      "request",
                      "incoterms",
                      "entityDefault"
                    ],
                    "description": "Which input signal determined `value` — 'request': the caller supplied importerOfRecord directly. 'incoterms': no importerOfRecord, but the caller supplied incoterms (DDP → SELLER, every other rule → BUYER). 'entityDefault': neither was supplied — resolved from the entity's configured defaultImporterOfRecord (see TaxSettings); a `source: 'entityDefault'` with `value` present is a machine-readable flag that this line's treatment was steered by the entity's configured default rather than anything the caller supplied on this specific request."
                  }
                }
              },
              "pvaAvailability": {
                "type": "object",
                "nullable": true,
                "description": "Present only for a transactionDirection: 'purchase' GOODS line whose resolved movement (see lineItems[].movement) is 'import' — Postponed VAT Accounting (PVA) context for the destination country. `state`/`source` are informational; `applied` is not: it is the destination's PVA state combined with the entity's own import-VAT-accounting election (see TaxSettings.importVatAccountingElection), and is exactly what determines whether this line's import VAT is self-assessed (`applied: true` — a selfAssessedOutputTax entry is present alongside a mirrored recoverable input-tax entry, never netted) or paid as cash import VAT at the border (`applied: false` — no self-assessed output-tax leg; the amount only ever surfaces as ordinary recoverable input tax). `null` (the whole object) when the destination has no seeded PVA-availability data covering this transaction's date — that case still resolves `applied: false` internally (never assumes a deferral with no seeded legal basis) but reports `null` here since there is no PVA fact to surface.",
                "required": [
                  "state",
                  "source",
                  "applied"
                ],
                "properties": {
                  "state": {
                    "type": "string",
                    "enum": [
                      "mandatory",
                      "optional",
                      "unavailable"
                    ]
                  },
                  "source": {
                    "type": "string",
                    "description": "Primary statute/notice/circular citation for `state`."
                  },
                  "applied": {
                    "type": "boolean",
                    "description": "Whether this line's import VAT is postponed (self-assessed) rather than paid in cash at the border — mandatory state always true; unavailable always false; optional follows the entity's own election (unset defaults to false)."
                  }
                }
              }
            }
          },
          "movement": {
            "type": "string",
            "enum": [
              "local",
              "intra_community",
              "export",
              "distance_sale",
              "import",
              "own_goods_movement"
            ],
            "description": "This line's own EU-VAT-Directive movement classification — the same vocabulary client tax codes are matched against (see ClientTaxCode.movement). A B2C goods sale from one EU country to another is `distance_sale` whether it is taxed at origin or at the destination (`local` only when genuinely domestic); an existing client tax code on movement `local` still matches such a sale taxed at your home-country rate when no `distance_sale` code exists, and a `distance_sale` code takes precedence. Present whenever this line reached full tax-decision derivation (the normal, non-degraded calculation path, for both 'sale' and 'purchase'); a NO_TAX_CODE outcome (see `outcome` below) always still carries this — the movement/treatment was genuinely determined even though no client tax code matched it. `export` covers every non-EU-EU B2B cross-border combination for a sale. For a purchase, `import` is a genuine EU/UK cross-border import of goods (Art. 30), `intra_community` an EU-EU intra-community acquisition (Arts. 20-23), and `own_goods_movement` a stock transfer of the entity's own goods (Art. 21) — see `outcome` and `sourcingRationale.importerOfRecord`/`.pvaAvailability` for how these are resolved."
          },
          "outcome": {
            "allOf": [
              {
                "$ref": "#/components/schemas/LineOutcome"
              }
            ],
            "description": "The headline per-line resolution verdict — the one field to check first to know whether this line's result is complete. Distinct from a whole-calculation rejection (e.g. category_not_supported): a NO_TAX_CODE or UNKNOWN_TREATMENT line never causes a request-level 422. `recoverability` is a further, separate, independent attribute of an already-resolved line — never a substitute headline for whether the line resolved at all. Optional: absent only on the rare engine-error fallback path; always present on the normal (non-degraded) calculation path."
          },
          "jurisdictionBreakdown": {
            "type": "array",
            "description": "This line's tax broken out by component, on every taxed line in every jurisdiction. The component rates add up to this line's rate and the taxAmounts to this line's taxAmount, to the cent (negative on a credit note). Outside the US one component for most jurisdictions (VAT 20% in FR, HST 13% in ON, GST 5% in AB) and several where a real split exists (QC: GST 5% + QST 9.975%; BC: GST + PST; MB: GST + RST; SK: GST + PST); in the US one per taxing authority (state/county/district/city/local). Absent on a line with no tax (exempt, zero-rated, reverse charge, out of scope). A rule that replaced the rate marks the components overridden.",
            "items": {
              "type": "object",
              "properties": {
                "level": {
                  "type": "string",
                  "description": "Outside the US COUNTRY, FEDERAL, PROVINCIAL, STATE, REGIONAL, COUNTY, CITY or DISTRICT; US: STATE, COUNTY, DISTRICT (county district), CITY, or LOCAL (city district).",
                  "example": "PROVINCIAL"
                },
                "name": {
                  "type": "string",
                  "description": "Label to show: the tax name outside the US (VAT, GST, HST, QST, PST, RST), the taxing-authority name in the US (e.g. \"CALIFORNIA\").",
                  "example": "QST"
                },
                "taxType": {
                  "type": "string",
                  "description": "Stable code: the tax code outside the US (VAT, GST, HST, QST, PST, RST, IGIC); in the US the tax_type code (01 state, 02 county, 03 county district, 04 city, 05 city district).",
                  "example": "QST"
                },
                "rate": {
                  "type": "number",
                  "description": "This component's own rate as a fraction, e.g. 0.09975 for QST 9.975%.",
                  "example": 0.09975
                },
                "taxAmount": {
                  "type": "number",
                  "description": "This component's share of the line's tax; negative on a credit note.",
                  "example": 9.98
                },
                "overridden": {
                  "type": "boolean",
                  "description": "true only when a rule replaced the rate."
                },
                "overrideRuleCode": {
                  "type": "string"
                },
                "previousRate": {
                  "type": "number",
                  "description": "The component's rate before the rule replaced it."
                }
              }
            }
          },
          "poLink": {
            "type": "string",
            "enum": [
              "NONE",
              "NOT_FOUND",
              "LINKED"
            ],
            "description": "transactionDirection: 'purchase' with documentStage: 'invoice' only — whether this line's own purchaseOrderId/purchaseOrderLineNumber resolved against a real, previously-calculated purchase_order-stage line."
          },
          "customProperties": {
            "type": "object",
            "additionalProperties": true,
            "description": "This line's customProperties bag, post-rule (the effective values the engine actually used, echoing back any rule-authored writes)."
          },
          "glAccount": {
            "type": "string",
            "description": "Purchase side. Post-rule value, including one carried through from the linked purchase order line. Omitted when the line has none."
          },
          "costCenter": {
            "type": "string",
            "description": "Purchase side. Post-rule value (see `glAccount`). Omitted when the line has none."
          },
          "intendedUse": {
            "type": "string",
            "description": "Purchase side. Post-rule value, including one carried through from the linked purchase order line when the request did not state it. Omitted when the line has none."
          },
          "accountAssignment": {
            "type": "string",
            "enum": [
              "stock",
              "expense",
              "asset",
              "project"
            ],
            "description": "Purchase side. Post-rule value; always one of the four. A raw ERP code the platform could not map is not echoed. Omitted when the line has none."
          },
          "sellerRegistration": {
            "type": "object",
            "description": "This line's own registration outcome — same shape as the top-level sellerRegistration (which describes only the cart's representative line). Answers why THIS line charged $0 or a flat rate in a mixed cart. Absent on the engine-error fallback path.",
            "properties": {
              "status": {
                "type": "string",
                "description": "e.g. REGISTERED, PENDING, NOT_REGISTERED."
              },
              "registrationNumber": {
                "type": "string"
              },
              "canCollectTax": {
                "type": "boolean"
              },
              "pricingModel": {
                "type": "string",
                "description": "e.g. TAX_EXCLUSIVE or TAX_INCLUSIVE."
              },
              "invoiceNote": {
                "type": "string"
              },
              "reason": {
                "type": "string",
                "description": "Explains why canCollectTax is false despite a registration being on file."
              },
              "reasonCode": {
                "type": "string",
                "description": "Machine-readable counterpart to `reason`."
              },
              "scheme": {
                "type": "string",
                "description": "Coverage provenance — names the registration/program that actually resolved this result (e.g. 'CO_SUTS' for a sale covered by Colorado's consolidated filing program) rather than leaving the caller to infer it from registrationNumber alone. Omitted (not null) whenever no scheme-bearing row was matched."
              },
              "schemeLabel": {
                "type": "string",
                "description": "Human-readable display label for `scheme`, when present."
              }
            }
          },
          "marketplaceFacilitator": {
            "type": "object",
            "description": "Present only on a calculation flagged isMarketplaceFacilitatedSale: true: the explicit marketplace-facilitator outcome for this line (never a silent zero). FACILITATOR_COLLECTS: the facilitator collects and remits, so this line carries tax 0 and taxCode 'O' for the seller. FACILITATOR_COLLECTS_STATE_TAX: the facilitator collects the state component and taxAmount is the local tax the seller still collects. SELLER_COLLECTS: the seller collects as on an unflagged sale and reasonCode says why.",
            "properties": {
              "outcome": {
                "type": "string",
                "enum": [
                  "FACILITATOR_COLLECTS",
                  "FACILITATOR_COLLECTS_STATE_TAX",
                  "SELLER_COLLECTS"
                ]
              },
              "reasonCode": {
                "type": "string",
                "enum": [
                  "MARKETPLACE_FACILITATOR_COLLECTS",
                  "MARKETPLACE_FACILITATOR_COLLECTS_STATE_TAX_ONLY",
                  "MARKETPLACE_FACILITATOR_NOT_YET_EFFECTIVE",
                  "MARKETPLACE_FACILITATOR_CATEGORY_EXCLUDED",
                  "MARKETPLACE_FACILITATOR_NOT_RELIEVED",
                  "MARKETPLACE_FACILITATOR_CONDITIONAL",
                  "MARKETPLACE_FACILITATOR_NO_STATE_SALES_TAX",
                  "MARKETPLACE_FACILITATOR_RULE_UNAVAILABLE",
                  "MARKETPLACE_FACILITATOR_LOCAL_SPLIT_UNAVAILABLE",
                  "MARKETPLACE_FACILITATOR_CUSTOMER_EXEMPTION_APPLIED",
                  "MARKETPLACE_FACILITATOR_NO_SELLER_TAX_DUE",
                  "MARKETPLACE_FACILITATOR_UNSUPPORTED_JURISDICTION"
                ]
              },
              "statusLabel": {
                "type": "string"
              },
              "jurisdiction": {
                "type": "object",
                "properties": {
                  "country": {
                    "type": "string"
                  },
                  "region": {
                    "type": "string",
                    "nullable": true
                  }
                }
              },
              "citation": {
                "type": "string",
                "nullable": true
              },
              "lawEffectiveDate": {
                "type": "string",
                "nullable": true
              },
              "coverage": {
                "type": "string",
                "nullable": true,
                "enum": [
                  "STATE_AND_LOCAL",
                  "STATE_ONLY",
                  "CONDITIONAL",
                  null
                ]
              },
              "sellerReporting": {
                "type": "string",
                "nullable": true
              },
              "reliefMode": {
                "type": "string",
                "enum": [
                  "FULL",
                  "CONDITIONAL_ASSERTED",
                  "NOT_RELIEVED"
                ],
                "description": "Absent when no rule row decided it."
              },
              "conditions": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Assumptions and seller obligations behind the state's relief."
              },
              "sellerRemainsLiableCases": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          "marketplaceFacilitatorTax": {
            "type": "object",
            "description": "Present only on a line the facilitator relieves: the tax the facilitator collects instead of the seller, as an informational memo (never part of taxAmount, summary.totalTax or any total).",
            "properties": {
              "amount": {
                "type": "number"
              },
              "rate": {
                "type": "number"
              },
              "jurisdictionBreakdown": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "level": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "taxType": {
                      "type": "string"
                    },
                    "rate": {
                      "type": "number"
                    },
                    "taxAmount": {
                      "type": "number"
                    }
                  }
                }
              }
            }
          },
          "overridden": {
            "type": "boolean",
            "description": "True when a rule replaced this line's resolved rate; see rateOverride. Absent otherwise."
          },
          "rateOverride": {
            "type": "object",
            "description": "Present on a line whose rate a POST_RATE_RESOLUTION rules-engine rule replaced. scope COMPONENT: only the named authority components changed (jurisdictionBreakdown is kept; changed rows carry overridden: true). scope TOTAL: the combined rate was replaced as a whole and the per-authority breakdown is dropped.",
            "properties": {
              "scope": {
                "type": "string",
                "enum": [
                  "COMPONENT",
                  "TOTAL"
                ]
              },
              "ruleCode": {
                "type": "string",
                "nullable": true,
                "description": "Code of the rule that made the (last) change."
              },
              "previousRate": {
                "type": "number",
                "description": "The combined rate the engine resolved before the override."
              },
              "rate": {
                "type": "number",
                "description": "The combined rate charged."
              }
            }
          },
          "partialTaxability": {
            "type": "object",
            "description": "Additive per-line explanation of a partially taxable US line.",
            "properties": {
              "reason": {
                "type": "string",
                "enum": [
                  "STATE_EXEMPT_LOCAL_TAXED",
                  "PORTION_EXEMPT_OVER_THRESHOLD",
                  "EXEMPT_BELOW_THRESHOLD",
                  "LOCAL_ELECTION",
                  "STATE_REDUCED_RATE",
                  "DELIVERY_IN_THRESHOLD",
                  "ALL_AUTHORITIES_EXEMPT"
                ]
              },
              "label": {
                "type": "string",
                "description": "Plain-language summary of reason."
              },
              "statutoryRate": {
                "type": "number",
                "description": "Sum of the applied component rates (before any taxableBasis)."
              },
              "taxedAmount": {
                "type": "number",
                "description": "The statutory base actually taxed (signed like taxableAmount)."
              },
              "exemptAmount": {
                "type": "number",
                "description": "taxableAmount minus taxedAmount."
              },
              "threshold": {
                "type": "object",
                "description": "The per-item value threshold that governed the line.",
                "properties": {
                  "amount": {
                    "type": "number"
                  },
                  "mode": {
                    "type": "string",
                    "enum": [
                      "WHOLE_ABOVE",
                      "WHOLE_AT_OR_ABOVE",
                      "EXCESS_ABOVE"
                    ]
                  },
                  "testedUnitPrice": {
                    "type": "number",
                    "description": "abs(unit price) less the per-unit line and order discount."
                  },
                  "quantity": {
                    "type": "number"
                  },
                  "basis": {
                    "type": "string",
                    "enum": [
                      "UNIT_PRICE",
                      "AMOUNT_OVER_QUANTITY",
                      "LINE_AMOUNT"
                    ],
                    "description": "LINE_AMOUNT: no unitPrice and no quantity, so a multi-unit line cannot be tested per unit."
                  }
                }
              },
              "exemptedComponents": {
                "type": "array",
                "description": "Authorities whose rows were dropped for this line.",
                "items": {
                  "type": "object",
                  "properties": {
                    "level": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "taxType": {
                      "type": "string"
                    },
                    "rate": {
                      "type": "number"
                    }
                  }
                }
              },
              "source": {
                "type": "string",
                "description": "content_source of the governing category-rate row."
              }
            }
          }
        }
      },
      "LineOutcome": {
        "type": "object",
        "description": "Discriminated-union per-line resolution result on lineItems[].outcome. `RESOLVED` never carries reasonCode/label/nextStep; `NO_TAX_CODE` and `UNKNOWN_TREATMENT` always do. Recoverability (lineItems[].recoverability) is a separate, secondary attribute of an already-resolved line — never a substitute for this field.",
        "required": [
          "status"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "RESOLVED",
              "NO_TAX_CODE",
              "UNKNOWN_TREATMENT"
            ],
            "description": "RESOLVED — this line got a complete, appliable tax decision; the default unless one of the two statuses below applies. NO_TAX_CODE — treatment (taxCode/rate/taxableAmount/taxAmount/totalAmount/movement) was fully determined, but no client tax code could be resolved for it; the computed tax result itself is real and unaffected — purely a \"nothing to hand back for your ERP mapping\" signal. UNKNOWN_TREATMENT — the engine could not fully determine this line's treatment at all (a required input signal is missing, or the scenario is outside current coverage) — always carries a reasonCode, and distinct from a whole-calculation rejection (e.g. category_not_supported): a line-local signal gap that never rejects the calculation, so every other line in a batch still finalizes."
          },
          "reasonCode": {
            "allOf": [
              {
                "$ref": "#/components/schemas/LineOutcomeReasonCode"
              }
            ],
            "description": "Absent when status is RESOLVED; always present otherwise."
          },
          "label": {
            "type": "string",
            "description": "Backend-authored, plain-language, copy-pasteable-to-a-UI summary of reasonCode. Absent when status is RESOLVED."
          },
          "nextStep": {
            "type": "string",
            "description": "Backend-authored, plain-language next step to move this line to RESOLVED. Absent when status is RESOLVED."
          }
        }
      },
      "LineOutcomeReasonCode": {
        "type": "string",
        "description": "Closed, backend-owned reason vocabulary for LineOutcome.reasonCode. Additive-only: a new value may be appended, but an existing one is never renamed or removed once shipped. Every value but `missing_supplier_country` is wired to real runtime logic; see that value's own x-enum-descriptions entry for why. Every reason applies to a transactionDirection: 'purchase' line.",
        "enum": [
          "missing_supplier_country",
          "missing_supplier_tax_id",
          "missing_supply_type",
          "missing_importer_of_record",
          "unsupported_jurisdiction",
          "low_value_gb",
          "foreign_established_supplier",
          "no_destination_registration",
          "own_goods",
          "no_matching_client_tax_code"
        ],
        "x-enum-descriptions": {
          "missing_supplier_country": "UNKNOWN_TREATMENT — the real supplier's country (supplier.billingAddress.country or supplier.shippingAddress.country) could not be determined at all. Reserved, not yet wired: this scenario currently rejects the whole calculation as 422 supplier_country_required_for_purchase (see that endpoint's 422 response) rather than surfacing as a per-line signal — kept in the closed enum ahead of that becoming a line-local outcome.",
          "missing_supplier_tax_id": "UNKNOWN_TREATMENT — a cross-border EU-EU GOODS purchase line (a real intra-community-acquisition candidate) whose supplier tax number is missing.",
          "missing_supply_type": "UNKNOWN_TREATMENT — the purchase line carries no supplyType, so goods vs. services sourcing can't be confirmed with confidence.",
          "missing_importer_of_record": "UNKNOWN_TREATMENT — importer-of-record resolution came up completely empty (no request field, no incoterms, no configured entity default) for an otherwise-genuine, cross-border-import GOODS purchase line.",
          "unsupported_jurisdiction": "UNKNOWN_TREATMENT — a genuine cross-border-import GOODS purchase line (buyer liable) whose destination is neither EU, GB, nor Northern Ireland.",
          "low_value_gb": "UNKNOWN_TREATMENT — a genuine cross-border-import GOODS purchase line whose destination is GB and whose GBP consignment value is at or under the UK's £135 low-value-consignment threshold — the UK treats this as point-of-sale, not import, VAT liability, which this engine does not yet compute; this keeps the line from ever reporting movement='import' for a shipment that legally isn't one.",
          "foreign_established_supplier": "UNKNOWN_TREATMENT — a domestic-looking GOODS purchase (ship-from country equals ship-to country) whose supplier VAT-ID country prefix disagrees with that address country — a genuine EU VAT Directive Art. 194 scenario whose reverse-charge rule varies by member state.",
          "no_destination_registration": "UNKNOWN_TREATMENT — a genuine EU-EU intra-community-acquisition candidate (third-party supplier, supplier tax number present) whose destination country the entity holds no active registration for.",
          "own_goods": "UNKNOWN_TREATMENT — the cross-border EU-EU GOODS purchase's supplier tax number matches one of this entity's own tax registrations — a stock transfer of the entity's own goods (Art. 21), never a third-party intra-community acquisition.",
          "no_matching_client_tax_code": "NO_TAX_CODE — treatment was fully determined; no client tax code (entity-configured) matched it."
        }
      },
      "TaxCalculationListItem": {
        "type": "object",
        "properties": {
          "companyCode": {
            "type": [
              "string",
              "null"
            ],
            "description": "The company code the calculation was recorded under, if any."
          },
          "id": {
            "type": "string"
          },
          "entityId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "jurisdictionCountry": {
            "type": "string"
          },
          "jurisdictionRegion": {
            "type": "string",
            "nullable": true
          },
          "transactionType": {
            "type": "string",
            "enum": [
              "invoice",
              "credit_note"
            ]
          },
          "totalAmount": {
            "type": "number",
            "nullable": true
          },
          "totalTax": {
            "type": "number",
            "nullable": true
          },
          "currency": {
            "type": "string",
            "nullable": true
          },
          "merchantRef": {
            "type": "string",
            "nullable": true
          },
          "paymentMethod": {
            "type": "string",
            "enum": [
              "card",
              "bank_transfer",
              "digital_wallet",
              "cash",
              "other"
            ],
            "nullable": true
          },
          "degraded": {
            "type": "boolean"
          },
          "resolvedAt": {
            "type": "string",
            "format": "date-time"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "sandbox": {
            "type": "boolean"
          },
          "transactionDirection": {
            "type": "string",
            "enum": ["sale", "purchase"]
          },
          "documentStage": {
            "type": "string",
            "enum": ["invoice", "purchase_order"],
            "description": "invoice, or purchase_order for a purchase-order estimate (excluded from every obligation total)."
          },
          "purchaseOrderId": {
            "type": "string",
            "nullable": true,
            "description": "The purchase-order reference: a purchase_order row's own id, otherwise the first line's purchaseOrderId (null when the calculation is not linked to a purchase order)."
          },
          "purchaseOrderLineNumber": {
            "type": "string",
            "nullable": true
          },
          "poLink": {
            "type": "object",
            "nullable": true,
            "description": "Cheap link summary (null on a purchase_order row). status is LINKED when a reference exists, otherwise NONE; the detail view resolves NOT_FOUND.",
            "properties": {
              "status": { "type": "string", "enum": ["NONE", "LINKED"] },
              "purchaseOrderId": { "type": "string", "nullable": true },
              "purchaseOrderLineNumber": { "type": "string", "nullable": true }
            }
          },
          "selfAssessedTaxAmount": {
            "type": "number",
            "description": "Tax the entity self-assessed on this purchase (reverse charge or postponed import VAT). 0 for sales, for purchases with none, and for rows recorded before this field existed."
          },
          "selfAssessedKind": {
            "type": "string",
            "nullable": true,
            "enum": ["REVERSE_CHARGE", "IMPORT_VAT_POSTPONED", "USE_TAX", null],
            "description": "Why the tax was self-assessed. USE_TAX is reserved for US consumer's use tax and is not returned yet."
          }
        }
      },
      "TaxCalcImportBatch": {
        "type": "object",
        "description": "A CSV bulk import batch — see POST /tax/calculate/import. Always async and always final: transactions are committed as they are processed (no preview or confirm step). Poll GET /tax/calculate/import/{batchId} for status.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "entityId": {
            "type": "string",
            "format": "uuid"
          },
          "accountId": {
            "type": "string",
            "format": "uuid"
          },
          "uploadedBy": {
            "type": "string",
            "nullable": true,
            "description": "Clerk user id, present only for a batch uploaded via the dashboard rather than this API."
          },
          "filename": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "UPLOADED",
              "PROCESSING",
              "COMPLETED",
              "FAILED",
              "CANCELLED"
            ]
          },
          "statusLabel": {
            "type": "string",
            "description": "Ready-to-render label for `status` — render directly, never re-derive one from `status`.",
            "example": "Processing…"
          },
          "blobPath": {
            "type": "string",
            "nullable": true,
            "description": "Internal storage location of the uploaded file. Opaque — not a fetchable URL."
          },
          "contentHash": {
            "type": "string",
            "nullable": true,
            "description": "SHA-256 of the uploaded file content."
          },
          "total": {
            "type": "integer",
            "description": "Transaction groups processed so far — final once status is COMPLETED."
          },
          "committedCount": {
            "type": "integer",
            "description": "Transactions committed so far — final once status is COMPLETED."
          },
          "errorCount": {
            "type": "integer",
            "description": "Transactions NOT committed because they errored."
          },
          "structuralError": {
            "type": "string",
            "nullable": true,
            "description": "Set when the whole file is rejected (e.g. a transactionRef re-appearing after its group already closed, or exceeding the row cap) rather than a per-transaction error. Transactions before the problem stay committed."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "completedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "TaxCalcImportTransaction": {
        "type": "object",
        "description": "One transaction group within a batch — one or more adjacent CSV rows sharing the same transactionRef.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "batchId": {
            "type": "string",
            "format": "uuid"
          },
          "entityId": {
            "type": "string",
            "format": "uuid"
          },
          "transactionRef": {
            "type": "string",
            "description": "Your own reference from the CSV's transactionRef column."
          },
          "rowStart": {
            "type": "integer",
            "description": "First CSV data row (1-indexed, header excluded) in this group."
          },
          "rowEnd": {
            "type": "integer"
          },
          "requestJson": {
            "description": "The built POST /tax/calculate request body for this group.",
            "type": "object"
          },
          "outcome": {
            "type": "string",
            "enum": [
              "COMMITTED",
              "ERROR"
            ]
          },
          "outcomeLabel": {
            "type": "string",
            "example": "Committed"
          },
          "calculationId": {
            "type": "string",
            "nullable": true,
            "description": "The committed calculation's id; null for ERROR rows — the same id GET /tax/calculate/{id}-style lookups use."
          },
          "errorCode": {
            "type": "string",
            "nullable": true
          },
          "errorMessage": {
            "type": "string",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Obligation": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "country": {
            "type": "string",
            "example": "US"
          },
          "region": {
            "type": "string",
            "nullable": true,
            "example": "CA"
          },
          "registrationStatus": {
            "type": "string",
            "enum": [
              "REGISTERED",
              "PENDING",
              "NOT_REGISTERED",
              "MONITORING"
            ],
            "example": "REGISTERED"
          },
          "registrationNumber": {
            "type": "string",
            "nullable": true,
            "example": "123-456-7890"
          },
          "obligationStatus": {
            "type": "string",
            "enum": [
              "COMPLIANT",
              "APPROACHING",
              "BREACH",
              "MONITORING"
            ],
            "example": "COMPLIANT"
          },
          "obligationType": {
            "type": "string",
            "nullable": true,
            "enum": [
              "THRESHOLD",
              "IMMEDIATE"
            ],
            "description": "IMMEDIATE means the jurisdiction has no revenue threshold — any sale at all creates the obligation. THRESHOLD is an ordinary revenue-threshold obligation. null covers jurisdictions with no threshold concept (e.g. OSS/IOSS)."
          },
          "actionRequired": {
            "type": "boolean",
            "description": "The authoritative signal for whether this obligation needs attention right now — true for a PENDING registration, a crossed threshold, an approaching threshold, or a zero-threshold obligation with sales already booked."
          },
          "statusLabel": {
            "type": "string",
            "description": "Ready-to-render status badge text, e.g. \"Registered\", \"Pending\", \"Registration required\", \"Approaching threshold\", \"Monitoring\". A crossed revenue threshold and a zero-threshold (IMMEDIATE) obligation with sales both render as \"Registration required\" — they represent the same required action.",
            "example": "Registration required"
          },
          "severity": {
            "type": "string",
            "enum": [
              "registered",
              "pending",
              "critical",
              "warning",
              "neutral"
            ],
            "description": "Severity tier backing statusLabel's colour — for display/sorting only, never a substitute for actionRequired."
          },
          "commentary": {
            "type": "string",
            "nullable": true,
            "description": "One-line plain-English guidance sentence explaining why action is needed. Only present (non-null) when actionRequired is true."
          },
          "thresholdAmount": {
            "type": "number",
            "nullable": true
          },
          "thresholdCurrency": {
            "type": "string",
            "nullable": true
          },
          "currentPeriodAmount": {
            "type": "number",
            "nullable": true
          },
          "estimatedExposure": {
            "type": "number",
            "nullable": true
          },
          "pendingCanCollect": {
            "type": "boolean",
            "description": "True when pending registration status still allows tax collection in this jurisdiction."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Product": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "tp_abc123"
          },
          "externalId": {
            "type": "string",
            "description": "Your product code (productCode) or an auto-assigned fallback ID."
          },
          "productName": {
            "type": "string",
            "nullable": true
          },
          "productDescription": {
            "type": "string",
            "nullable": true
          },
          "suggestedSlug": {
            "type": "string",
            "nullable": true,
            "description": "Resolved tax category slug.",
            "example": "saas"
          },
          "confidence": {
            "type": "number",
            "nullable": true,
            "description": "AI classification confidence (0–1). Only populated when tier is AI_CLASSIFIED — null for a human-confirmed, standard-mapped, or unclassified product, since a percentage would misrepresent something that either isn't a guess or never produced a real answer.",
            "example": 0.92
          },
          "status": {
            "type": "string",
            "enum": [
              "APPROVED",
              "NEEDS_REVIEW",
              "MANUAL",
              "PENDING"
            ],
            "example": "APPROVED"
          },
          "statusLabel": {
            "type": "string",
            "description": "Ready-to-render display label for `status`.",
            "example": "Approved"
          },
          "classificationSource": {
            "type": "string",
            "nullable": true,
            "enum": [
              "MANUAL",
              "STANDARD_MAPPING",
              "AI",
              "AI_FALLBACK"
            ],
            "description": "MANUAL after taxCategory was supplied in the request or a human review update; STANDARD_MAPPING when resolved from the maintained classification code map (e.g. Stripe tax codes, Shopify categories, customs codes); AI for a successful AI classification; AI_FALLBACK when AI classification failed and a conservative default was used."
          },
          "tier": {
            "type": "string",
            "enum": [
              "CONFIRMED",
              "STANDARD_MAPPING",
              "AI_CLASSIFIED",
              "UNCLASSIFIED"
            ],
            "description": "Trust tier derived from classificationSource. CONFIRMED = a human declared this true (no confidence shown). STANDARD_MAPPING = resolved from a maintained external code list (no confidence shown). AI_CLASSIFIED = a real AI classification attempt (confidence is meaningful). UNCLASSIFIED = classification never produced a real answer and should be reviewed."
          },
          "tierLabel": {
            "type": "string",
            "description": "Ready-to-render display label for `tier`.",
            "example": "Confirmed"
          },
          "addedLabel": {
            "type": "string",
            "description": "Ready-to-render description of how and when this product entered the catalogue, e.g. \"Uploaded by Jane Doe\" or \"Synced from WooCommerce\"."
          },
          "confirmedByLabel": {
            "type": "string",
            "nullable": true,
            "description": "Ready-to-render description of who confirmed this classification, or null if it has not been confirmed by a human."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CatalogProduct": {
        "type": "object",
        "description": "A reusable catalogue product/service record, referenceable from invoice line items — the same underlying tax_products table as /tax/products, exposed here under a shorter, generic-REST-shaped path. `sku` is the account-scoped idempotency key: re-posting the same sku (and entityId) updates the existing row via ON CONFLICT rather than creating a duplicate; a null sku always creates a new row.",
        "properties": {
          "id": {
            "type": "string",
            "description": "tp_ prefix.",
            "example": "tp_abc123xyz"
          },
          "entityId": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "Null for an account-wide product not tied to one entity."
          },
          "name": {
            "type": "string",
            "example": "Annual SaaS subscription"
          },
          "sku": {
            "type": "string",
            "nullable": true,
            "description": "Your own product SKU or identifier. Used as the idempotency key on create — re-posting the same sku (scoped to the same entityId) updates the record instead of creating a duplicate.",
            "example": "SKU-001"
          },
          "description": {
            "type": "string",
            "nullable": true,
            "example": "Cloud-based ERP software, billed annually."
          },
          "taxCategory": {
            "type": "string",
            "nullable": true,
            "description": "Tax category slug — AI-classified from name/description/sku unless explicitly overridden at create/update time."
          },
          "confidence": {
            "type": "number",
            "nullable": true,
            "description": "AI classification confidence (0–1). Null once a human/manual value has taken over (status MANUAL) or for a classification with no AI confidence signal to report."
          },
          "status": {
            "type": "string",
            "enum": [
              "APPROVED",
              "NEEDS_REVIEW",
              "MANUAL",
              "PENDING"
            ],
            "description": "Review-workflow state — MANUAL once a human has ever set taxCategory explicitly via this API/dashboard, thereafter never silently overwritten by a later AI reclassification."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Entity": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "example": "Acme Inc."
          },
          "country": {
            "type": "string",
            "minLength": 2,
            "maxLength": 2,
            "example": "US"
          },
          "vatNumber": {
            "type": "string",
            "nullable": true,
            "example": "DE123456789"
          },
          "extraFields": {
            "type": "object",
            "nullable": true,
            "additionalProperties": {
              "type": "string"
            },
            "description": "Country-specific profile fields (e.g. IT regimeFiscale, FR siret, DE steuernummer) stored on the entity's home-country registration. Present on GET/POST /entities (list and create); not yet returned by GET/PATCH /entities/{id}.",
            "example": {
              "regimeFiscale": "RF01"
            }
          },
          "isDefault": {
            "type": "boolean"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "address": {
            "type": "object",
            "properties": {
              "line1": {
                "type": "string",
                "nullable": true
              },
              "line2": {
                "type": "string",
                "nullable": true
              },
              "city": {
                "type": "string",
                "nullable": true
              },
              "postalCode": {
                "type": "string",
                "nullable": true
              }
            }
          },
          "solutions": {
            "type": "object",
            "description": "Map of solution codes to enabled status (e.g. { einvoicing: true, tax: false }). `compliance_radar` is true on every plan below Enterprise; on Enterprise the account administrator selects it, and when false thresholds and mandate dates are not monitored.",
            "additionalProperties": {
              "type": "boolean"
            }
          },
          "registrationsConfirmedNone": {
            "type": "boolean",
            "description": "True when this entity has explicitly confirmed it has no tax registrations anywhere yet (set via PATCH confirmNoRegistrations) — satisfies the add-registration onboarding step without a real registration on file."
          },
          "notifyCustomerByDefault": {
            "type": "boolean",
            "description": "Default for the customer invoice notification feature (see notifyCustomer on POST /invoices) — applied whenever a send omits the per-request override. Set via PATCH /entities/{id}."
          },
          "mxIngestionMode": {
            "type": "string",
            "enum": [
              "sat_pull",
              "client_push"
            ],
            "description": "Mexico only. Defaults to sat_pull for every entity — meaningless (but always present, defaulted) for a non-MX entity. See PATCH /entities/{id} for how to change it."
          },
          "mxIngestionStartDate": {
            "type": "string",
            "format": "date",
            "nullable": true,
            "description": "Mexico only. See PATCH /entities/{id}."
          },
          "contactName": {
            "type": "string",
            "nullable": true,
            "description": "Seller contact name shown on this entity's invoices — invoice CONTENT, required (with contactEmail, contactPhone) for German XRechnung (BR-DE-2/5/6). Set via PATCH /entities/{id}."
          },
          "contactEmail": {
            "type": "string",
            "nullable": true,
            "description": "Seller contact email shown on this entity's invoices. Invoice CONTENT — independent of replyToEmail, which is notification-delivery config, not invoice content. Set via PATCH /entities/{id}."
          },
          "contactPhone": {
            "type": "string",
            "nullable": true,
            "description": "Seller contact phone shown on this entity's invoices. Set via PATCH /entities/{id}."
          },
          "replyToEmail": {
            "type": "string",
            "nullable": true,
            "description": "Where a customer's reply to the buyer-invoice-notification email goes. Deliberately independent of contactEmail. Set via PATCH /entities/{id}."
          },
          "entityFacts": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Facts about the entity itself (not any one country registration) — e.g. { \"legalForm\": \"GMBH\" }. See GET /entities/fact-definitions for known keys. Set via PATCH /entities/{id}."
          },
          "logoUrl": {
            "type": "string",
            "format": "uri",
            "nullable": true,
            "description": "The entity's own uploaded logo, rendered on the buyer-facing invoice-notification email, the hosted invoice-view page, and the downloadable invoice PDF, in place of the platform's own logo. A ready-to-fetch URL through an app-mediated route — never a raw storage URL. Null when no logo is set. Managed via POST/DELETE /entities/{id}/logo, not PATCH."
          },
          "defaultDeInvoiceFormat": {
            "type": "string",
            "nullable": true,
            "enum": [
              "ZUGFERD",
              "XRECHNUNG",
              "PEPPOL",
              null
            ],
            "description": "Entity-level default German invoice format, applied by POST /send when neither the request nor the customer sets countrySpecific.de.invoiceFormat. PEPPOL delivers a Peppol BIS Billing 3.0 invoice over the Peppol network. Null clears the override, falling through to the platform default (ZUGFERD)."
          }
        }
      },
      "MxSatStatus": {
        "type": "object",
        "description": "SAT's ConsultarEstatusCFDI outcome — the primary source of truth for clearanceStatus on a Mexico record.",
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "vigente",
              "cancelado",
              "not_found",
              "service_unavailable"
            ]
          },
          "checkedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "MxFlag": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "enum": [
              "MX_RECEIVER_REGIME_MISMATCH",
              "MX_RECEIVER_CP_MISMATCH",
              "MX_EFOS_LISTED"
            ]
          },
          "message": {
            "type": "string"
          }
        }
      },
      "MxParty": {
        "type": "object",
        "nullable": true,
        "properties": {
          "taxId": {
            "type": "string"
          },
          "name": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "MxTotals": {
        "type": "object",
        "nullable": true,
        "properties": {
          "total": {
            "type": "number"
          },
          "taxExclusiveAmount": {
            "type": "number"
          },
          "taxTotal": {
            "type": "number",
            "nullable": true,
            "description": "Total transferred tax (IVA traslados) on the CFDI (formerly `vatTotal`)."
          },
          "retencionesTotal": {
            "type": "number",
            "nullable": true
          }
        }
      },
      "MxInboundCfdiResult": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Clearvo's internal invoice id — the same id GET /invoices/{id} uses."
          },
          "uuid": {
            "type": "string",
            "description": "The CFDI's own Folio Fiscal (UUID) — the legal reference."
          },
          "clearanceStatus": {
            "type": "string",
            "enum": [
              "ACCEPTED",
              "CANCELLED",
              "PENDING"
            ]
          },
          "satStatus": {
            "$ref": "#/components/schemas/MxSatStatus"
          },
          "flags": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MxFlag"
            }
          },
          "issuer": {
            "$ref": "#/components/schemas/MxParty"
          },
          "receiver": {
            "$ref": "#/components/schemas/MxParty"
          },
          "totals": {
            "$ref": "#/components/schemas/MxTotals"
          },
          "sourceChannel": {
            "type": "string",
            "enum": [
              "MX_SAT_INBOUND",
              "MX_CLIENT_PUSH_INBOUND"
            ]
          },
          "duplicate": {
            "type": "boolean",
            "description": "True when an already-stored terminal row for this UUID was returned as-is — no re-processing, no second webhook."
          }
        }
      },
      "MxPollResult": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "enabled": {
            "type": "boolean",
            "description": "False when the account-level SAT-poll kill switch is off."
          },
          "entitiesPolled": {
            "type": "integer"
          },
          "totalNewInvoices": {
            "type": "integer"
          },
          "totalCancelled": {
            "type": "integer"
          },
          "results": {
            "type": "array",
            "items": {
              "type": "object"
            }
          }
        }
      },
      "MxSyncStatus": {
        "type": "object",
        "properties": {
          "entityId": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "not_started",
              "ok",
              "stale",
              "error"
            ]
          },
          "label": {
            "type": "string"
          },
          "lastCheckedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "lastSuccessAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "lastError": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "MxCredentialStatus": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "entityId": {
            "type": "string"
          },
          "rfc": {
            "type": "string",
            "description": "The e.firma certificate's own RFC."
          },
          "certificateNotAfter": {
            "type": "string",
            "format": "date-time"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "message": {
            "type": "string"
          }
        }
      },
      "BrFlag": {
        "type": "object",
        "description": "A non-blocking WARNING-severity finding attached to a BR record or webhook payload — SEFAZ's own protNFe/nProt remains the authoritative proof of clearance regardless of any flag here; a BrFlag is never a reason a document is rejected or held.",
        "properties": {
          "code": {
            "type": "string",
            "enum": [
              "BR_SIGNATURE_INVALID",
              "BR_CHAIN_UNVERIFIED",
              "BR_CCE_PRESENT",
              "BR_DENIED_CHECK_OWN_REGISTRATION",
              "BR_DEST_CNPJ_UNMATCHED",
              "BR_DIGEST_MISMATCH",
              "BR_CREDIT_EVENT"
            ]
          },
          "severity": {
            "type": "string",
            "enum": [
              "warning"
            ]
          },
          "message": {
            "type": "string"
          }
        }
      },
      "BrCredentialStatus": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "entityId": {
            "type": "string"
          },
          "cnpj": {
            "type": "string",
            "description": "The e-CNPJ certificate's own CNPJ."
          },
          "certificateNotAfter": {
            "type": "string",
            "format": "date-time"
          },
          "autoCiencia": {
            "type": "boolean"
          },
          "manifestationRegime": {
            "type": "string",
            "enum": [
              "general"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "message": {
            "type": "string"
          }
        }
      },
      "BrPollResult": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "enabled": {
            "type": "boolean",
            "description": "False when the account-level Distribuição DFe poll kill switch is off — zero SEFAZ calls are made either way."
          },
          "entitiesPolled": {
            "type": "integer"
          },
          "cycleId": {
            "type": "string",
            "description": "This sweep call's own hour-truncated dedupe id — every digest sent during this call shares it."
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BrPollEntityOutcome"
            }
          }
        }
      },
      "BrPollEntityOutcome": {
        "type": "object",
        "properties": {
          "entityId": {
            "type": "string"
          },
          "newFullCount": {
            "type": "integer",
            "description": "New full procNFe documents stored this cycle."
          },
          "newSummaryCount": {
            "type": "integer",
            "description": "New resNFe summaries stored this cycle."
          },
          "cienciaCount": {
            "type": "integer",
            "description": "Ciência events auto-registered this cycle."
          },
          "credentialInvalid": {
            "type": "boolean"
          },
          "skipped": {
            "type": "string",
            "nullable": true,
            "enum": [
              "not_homologated",
              "br_uf_missing"
            ],
            "description": "Present when the entity was skipped before any SEFAZ call was attempted."
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "BrSyncStatus": {
        "type": "object",
        "properties": {
          "entityId": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "not_started",
              "ok",
              "stale",
              "error"
            ]
          },
          "label": {
            "type": "string"
          },
          "hint": {
            "type": "string",
            "nullable": true
          },
          "lastCheckedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "lastSuccessAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "lastError": {
            "type": "string",
            "nullable": true
          },
          "certificateNotAfter": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "certificateExpiringSoon": {
            "type": "boolean"
          }
        }
      },
      "TaxSettings": {
        "type": "object",
        "properties": {
          "defaultCompanyCode": {
            "type": [
              "string",
              "null"
            ],
            "description": "Entity-level default company code (Enterprise): the code a calculation or send that names none is recorded under. One of the entity's active codes from GET /company-codes, or null. Changing it below Enterprise is HTTP 403 company_codes_not_included; an unknown or archived code is HTTP 422 company_code_unknown."
          },
          "vatUnverifiableTreatment": {
            "type": "string",
            "enum": [
              "consumer",
              "business"
            ],
            "description": "How to treat buyers whose VAT number cannot be verified in real time. consumer=charge standard rate; business=treat as B2B (reverse charge or zero-rate). Account-wide default; can be overridden per-request via vatUnverifiableFallback."
          },
          "vatValidationMode": {
            "type": "string",
            "enum": [
              "full",
              "format",
              "none"
            ],
            "description": "How strictly buyer VAT numbers are checked before applying reverse charge. full=live validation against the issuing authority (e.g. VIES); format=structural check only, no network call; none=skip validation entirely."
          },
          "defaultPriceIncludesTax": {
            "type": "boolean",
            "description": "Whether prices sent to POST /tax/calculate already include tax (true) or are tax-exclusive (false), for every jurisdiction OTHER than the US — defaults to true (inclusive), matching common non-US B2C convention. Always ignored for a US-resolved line, which is always treated as exclusive at this account-wide tier regardless of this value — US retail convention adds tax at checkout rather than folding it into the sticker price. A line item's own amountIncludesTax field is honoured for every jurisdiction including the US (e.g. a fixed-price digital subscription or a marketplace absorbing tax)."
          },
          "defaultTaxCategorySlug": {
            "type": "string",
            "nullable": true,
            "description": "Default tax category slug applied to line items that have no explicit taxCategory. Must be a valid slug from GET /v1/tax/categories.",
            "example": "saas"
          },
          "usAddressPrecision": {
            "type": "string",
            "enum": [
              "rooftop",
              "zip"
            ],
            "description": "How US sales tax jurisdiction is resolved. rooftop=resolve the full street address for the most accurate rate; zip=ZIP code only, no added latency but less precise near jurisdiction boundaries."
          },
          "dutiesEnabled": {
            "type": "boolean",
            "default": false,
            "description": "Estimated import duty, import VAT/GST and customs fees on cross-border carts, returned as a separate estimate that is never added to totalTax or totalAmountWithTax. Off by default; a request's duties.include overrides it either way."
          },
          "dutyRatePolicyForHs6": {
            "type": "string",
            "enum": [
              "modal",
              "highest"
            ],
            "default": "modal",
            "description": "Which rate to show when a product code is only known to six digits and the national tariff has several rates under it: modal (the most common, the default) or highest (the conservative figure). Applies to estimates only; a deposit collected from the buyer always uses the highest rate."
          },
          "defaultCountryOfOrigin": {
            "type": "string",
            "nullable": true,
            "pattern": "^[A-Za-z]{2}$",
            "description": "Two-letter country the goods are made in, used for a duty estimate when neither the line item nor the product catalogue states one. Never inferred from the ship-from address."
          },
          "placeOfBusinessAddress": {
            "type": "object",
            "nullable": true,
            "description": "Where your business is physically established — distinct from the per-transaction seller address sent to POST /v1/tax/calculate. Changing this can change which jurisdiction's rate applies to your intrastate US sales: Texas, California, and Illinois source some sales to the seller's own location rather than the buyer's. Review recent tax calculations after changing this field, since it can shift the tax outcome of future transactions in those states. Each sub-field is independently settable to null to clear just that field; pass null for the whole object to clear the entire address.",
            "properties": {
              "line1": {
                "type": "string",
                "nullable": true,
                "example": "100 Congress Ave"
              },
              "line2": {
                "type": "string",
                "nullable": true,
                "example": "Suite 2000"
              },
              "city": {
                "type": "string",
                "nullable": true,
                "example": "Austin"
              },
              "region": {
                "type": "string",
                "nullable": true,
                "example": "TX"
              },
              "postalCode": {
                "type": "string",
                "nullable": true,
                "example": "78701"
              },
              "country": {
                "type": "string",
                "nullable": true,
                "minLength": 2,
                "maxLength": 3,
                "description": "2 or 3 letter country code.",
                "example": "US"
              }
            }
          },
          "isMarketplaceFacilitator": {
            "type": "boolean",
            "default": false,
            "description": "Whether the account is a marketplace facilitator (a platform that collects and remits tax on behalf of third-party sellers) rather than a direct seller. Currently only affects sourcing determination for Illinois — no other jurisdiction is affected by this flag today."
          },
          "confirmedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When the account holder last explicitly confirmed these settings. Null means they have not yet reviewed them. Set via PATCH with confirmed=true.",
            "readOnly": true
          },
          "confirmed": {
            "type": "boolean",
            "description": "PATCH-only. Set true to stamp confirmedAt, marking the settings as explicitly reviewed — independent of whether any other field changed in the same request.",
            "writeOnly": true
          },
          "defaultImporterOfRecord": {
            "type": "string",
            "nullable": true,
            "enum": [
              "SELLER",
              "BUYER"
            ],
            "description": "Entity-grain-only setting (no account-level tier) resolved onto a transactionDirection: 'purchase' calculation's importerOfRecord when neither the request nor incoterms supplies one — see the importerOfRecord request field's own description for the full request > incoterms > entityDefault precedence chain. `null` (the default) means unset — no fallback fires, so an otherwise-genuine cross-border-import GOODS purchase line with no signal at any tier is flagged lineItems[].outcome.reasonCode 'missing_importer_of_record' rather than guessed. PATCH with null to clear."
          },
          "importVatAccountingElection": {
            "type": "string",
            "nullable": true,
            "enum": [
              "POSTPONED",
              "STANDARD"
            ],
            "description": "Entity-grain-only setting (no account-level tier): which import-VAT-accounting mechanism applies in jurisdictions where it is genuinely elective (e.g. UK/IE Postponed VAT Accounting — PVA). 'POSTPONED' means import VAT is accounted for on the periodic VAT return rather than paid at the border; 'STANDARD' means the ordinary pay-then-reclaim-at-import treatment applies. Consulted only for a destination whose PVA availability is 'optional' (see lineItems[].sourcingRationale.pvaAvailability on POST /tax/calculate) — a 'mandatory' destination always postpones and an 'unavailable' one never does, regardless of this setting. `null` (the default) means no election has been made — an 'optional' destination then defaults to cash-at-the-border (never assumes a deferral with no legal basis). PATCH with null to clear."
          },
          "apTolerance": {
            "type": "object",
            "nullable": true,
            "description": "Purchase-side (AP) vendor-charged-tax verification tolerance. Set to { absoluteAmount, percent } (both non-negative), or null to clear it back to the platform default (zero tolerance). Changing it is Enterprise only (Purchases (AP) sub-solution): 403 `ap_not_included` or `ap_not_enabled` otherwise; resending the current value is accepted unchanged.",
            "properties": {
              "absoluteAmount": {
                "type": "number",
                "minimum": 0
              },
              "percent": {
                "type": "number",
                "minimum": 0
              }
            }
          },
          "apPoDisposition": {
            "type": "string",
            "nullable": true,
            "enum": [
              "warn",
              "hold",
              "ignore"
            ],
            "description": "How a purchase-order fact mismatch (poComparison) affects the AP decision outcome. warn (default) surfaces it without blocking, hold forces outcome HELD, ignore skips PO comparison for outcome purposes. null clears it back to the platform default ('warn'). Changing it is Enterprise only (Purchases (AP) sub-solution): 403 `ap_not_included` or `ap_not_enabled` otherwise."
          }
        }
      },
      "TaxCategory": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string",
            "example": "saas"
          },
          "name": {
            "type": "string",
            "example": "Software as a Service"
          },
          "defaultTaxCode": {
            "type": "string",
            "description": "Default EN16931 tax code for this category (S, AA, E, etc.).",
            "example": "S"
          },
          "eligibleSchemes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Registration schemes this category is relevant for (e.g. OSS, STANDARD)."
          }
        }
      },
      "ReportingObligation": {
        "type": "object",
        "description": "One customer-toggleable reporting regime row for an entity (entity_reporting_obligations). Regime codes are the platform's canonical lowercase regime registry — GET surfaces only the toggleable subset (fr_ereporting, es_sii, pt_efatura).",
        "properties": {
          "obligation": {
            "type": "string",
            "enum": [
              "fr_ereporting",
              "es_sii",
              "pt_efatura"
            ],
            "description": "Regime code. pt_efatura is Portugal's invoice-data communication to AT (Autoridade Tributária, Decreto-Lei n.º 198/2012) — a per-document, real-time reporting obligation layered on top of the always-on certified-issuance mandate; when enabled, a PT sale dispatches under mandate PT_EFATURA instead of PT_EINVOICING (POST /v1/send's response then carries a `reporting` block, GET /v1/invoices/{id} a `ptReportingDetail` block)."
          },
          "country": {
            "type": "string",
            "description": "ISO 3166-1 alpha-2 country the regime applies to.",
            "example": "ES"
          },
          "enabled": {
            "type": "boolean",
            "description": "false when no row exists — never derived, never defaulted on."
          },
          "registered": {
            "type": "boolean",
            "description": "Whether the entity holds a current STANDARD tax registration in that country. When false, an enabling PATCH for this regime is refused with 400 REGISTRATION_REQUIRED (disabling is never gated) — `registrationGate` carries the message and fixUrl to send the user to first."
          },
          "effectiveFrom": {
            "type": "string",
            "format": "date",
            "nullable": true,
            "description": "Date the obligation started (or starts) applying. Null when the regime has never been enabled."
          },
          "submissionMode": {
            "type": "string",
            "nullable": true,
            "enum": [
              "immediate",
              "batch_auto",
              "batch_review"
            ],
            "description": "How transactions under this regime are dispatched. es_sii allows `immediate`, `batch_auto` and `batch_review`; fr_ereporting allows `batch_review` (you confirm each period) and `batch_auto` (a period you have not confirmed is sent automatically the day before its filing deadline; you can still confirm earlier), never `immediate`; `defaultSubmissionMode` (batch_review for both) is what a new enable starts on when none is chosen. pt_efatura is `immediate`-only (each invoice is communicated to AT in real time — there is no period to batch against). Null when never set. Changing the mode never affects a batch that is already open — it completes under its original mode (see `openBatch`/`modeChangeNotice`)."
          },
          "setupStatus": {
            "type": "string",
            "nullable": true,
            "enum": [
              "not_started",
              "awaiting_customer",
              "awaiting_authority",
              "ready",
              "blocked"
            ],
            "description": "Ops-maintained setup progress for this regime (e.g. AEAT SII census enrolment / Annex I authorisation). Read-only on the public surface."
          },
          "setupStatusNote": {
            "type": "string",
            "nullable": true,
            "description": "Free-text ops note accompanying setupStatus. Read-only on the public surface."
          },
          "label": {
            "type": "string",
            "description": "Display label for the regime, e.g. \"Spain · SII\".",
            "example": "Spain · SII"
          },
          "help": {
            "type": "string",
            "description": "One-paragraph plain-language description of the regime, ready to render."
          },
          "allowedSubmissionModes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "immediate",
                "batch_auto",
                "batch_review"
              ]
            },
            "description": "Submission modes this regime accepts on PATCH (SUBMISSION_MODE_NOT_ALLOWED otherwise). es_sii: all three; fr_ereporting: `batch_review` and `batch_auto`; pt_efatura: `immediate` only."
          },
          "defaultSubmissionMode": {
            "type": "string",
            "nullable": true,
            "enum": [
              "immediate",
              "batch_auto",
              "batch_review"
            ],
            "description": "The mode a new enable should preselect when the row carries no submissionMode of its own — `batch_review` for es_sii and fr_ereporting (nothing is filed until a human confirms the batch), `immediate` for pt_efatura."
          },
          "submissionModeLabel": {
            "type": "string",
            "nullable": true,
            "description": "Human label for submissionMode (\"Send immediately\", \"Batch, auto-send\", \"Batch, review first\"). Use `vocabulary.submissionModes` for the full list with descriptions."
          },
          "setupStatusLabel": {
            "type": "string",
            "nullable": true,
            "description": "Human label for setupStatus."
          },
          "setupStatusNextStep": {
            "type": "string",
            "nullable": true,
            "description": "Plain-language next step for the current setupStatus, when one applies."
          },
          "setupStatusTone": {
            "type": "string",
            "nullable": true,
            "enum": [
              "neutral",
              "warning",
              "info",
              "success",
              "danger"
            ],
            "description": "Semantic tone for rendering setupStatus."
          },
          "notes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Plain-language facts about this row's current state (e.g. SII outranking VeriFactu from the start date; the registration warning below when it applies)."
          },
          "registrationGate": {
            "type": "object",
            "nullable": true,
            "description": "Non-null whenever `registered` is false, regardless of `enabled`: the row cannot be meaningfully enabled yet. Send the user to `fixUrl` (an absolute, tenant-aware URL to the Registrations page pre-filtered to this country, returning to the Jurisdictions page) before attempting to enable — a PATCH attempted anyway fails 400 REGISTRATION_REQUIRED with the same fixUrl.",
            "properties": {
              "message": {
                "type": "string"
              },
              "fixUrl": {
                "type": "string",
                "format": "uri"
              }
            }
          },
          "registrationWarning": {
            "type": "object",
            "nullable": true,
            "description": "Non-null only when this row is ENABLED but the entity no longer has a current registration in the country (deregistered or lapsed after the fact). The obligation row is never auto-disabled; submissions are held until the registration is restored. `fixUrl` is a tenant-relative path (\"/registrations?country=ES\").",
            "properties": {
              "message": {
                "type": "string"
              },
              "fixUrl": {
                "type": "string"
              }
            }
          },
          "openBatch": {
            "type": "boolean",
            "description": "True when this regime currently has an OPEN reporting batch accumulating for the entity (es_sii batch_auto/batch_review) — see GET /v1/reporting-batches?status=open."
          },
          "modeChangeNotice": {
            "type": "string",
            "nullable": true,
            "description": "Non-null exactly when openBatch is true — plain-language warning that changing submissionMode will not affect the batch already running; it completes under its original mode and the new setting applies from the next batch onward."
          }
        }
      },
      "ReportingBatch": {
        "type": "object",
        "description": "One reporting batch: for Spain SII, the group of registros an entity with es_sii submissionMode `batch_auto` or `batch_review` accumulates and sends to AEAT together (per entity and SII book); for France e-reporting (`obligation: fr_ereporting`, `batch_review` by default or `batch_auto`), the transactions of one reporting period (a décade, month or two-month window depending on VAT regime), one batch per direction (sales Issued, purchases Received). For France a batch is submitted when you confirm it, or, only if you chose `batch_auto`, automatically the day before its filing deadline. Its deadline (`reportBy` = `periodEnd`) is the earliest AEAT report-by date across the records it holds, so a batch is always sent in time for its most urgent record. Lifecycle: open → closed (accumulation stopped by the daily sweep, which runs on Spanish business days and triggers at the batch's report-by date) → ready_for_review (batch_review: frozen for your confirm) or straight to submitted (batch_auto) → filed (AEAT has answered every record). Render statuses with `vocabulary.periodStatuses` rather than hardcoding labels.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Batch UUID — the path parameter for the detail/confirm/exclude endpoints."
          },
          "batchId": {
            "type": "string",
            "description": "Human-readable batch key (the same value POST /send returns as `batchId` and the reporting_batch.* webhooks carry).",
            "example": "ES-SII-2026-09-ISSUED-1"
          },
          "entityId": {
            "type": "string",
            "format": "uuid"
          },
          "country": {
            "type": "string",
            "example": "ES"
          },
          "obligation": {
            "type": "string",
            "example": "es_sii"
          },
          "book": {
            "type": "string",
            "nullable": true,
            "enum": [
              "issued",
              "received"
            ],
            "description": "SII ledger book (Libro Registro de Facturas Expedidas / Recibidas)."
          },
          "direction": {
            "type": "string",
            "enum": [
              "sale",
              "purchase"
            ],
            "description": "Which book this batch reports. A period holds one direction, so a period with both sales and purchases is two batch rows, each with its own status and deadline. For SII it follows `book`."
          },
          "directionLabel": {
            "type": "string",
            "description": "Plain-language label for `direction`: \"Issued\" or \"Received\"."
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "closed",
              "ready_for_review",
              "submitted",
              "filed"
            ]
          },
          "statusLabel": {
            "type": "string",
            "description": "Human label for status."
          },
          "submissionMode": {
            "type": "string",
            "nullable": true,
            "enum": [
              "batch_auto",
              "batch_review"
            ],
            "description": "The es_sii submissionMode the batch was opened under. Fixed for the life of the batch — a later change to the obligation applies from the next batch onward."
          },
          "submissionModeLabel": {
            "type": "string",
            "nullable": true
          },
          "periodStart": {
            "type": "string",
            "format": "date",
            "nullable": true,
            "description": "Earliest record issue date in the batch."
          },
          "periodEnd": {
            "type": "string",
            "format": "date",
            "nullable": true,
            "description": "The batch's filing deadline — same value as reportBy."
          },
          "reportBy": {
            "type": "string",
            "format": "date",
            "nullable": true,
            "description": "Batch-level AEAT deadline: the earliest report-by date across every record in the batch."
          },
          "overdueLabel": {
            "type": "string",
            "nullable": true,
            "description": "Server-computed \"N Spanish business day(s) overdue\" for the batch's own reportBy against today; null when not overdue. Never reconstruct this client-side."
          },
          "recordCount": {
            "type": "integer"
          },
          "submittedLate": {
            "type": "boolean",
            "description": "True when the batch was (or is being) submitted after its reportBy — set the one time the batch_auto submitter gave up past the deadline, or a late confirm."
          },
          "closedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "submittedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "filedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "readyAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When the batch entered ready_for_review (batch_review only)."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ReportingBatchDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ReportingBatch"
          }
        ],
        "type": "object",
        "description": "Full batch detail: every ReportingBatch field plus the review/concurrency fields and the records.",
        "properties": {
          "reviewedBy": {
            "type": "string",
            "nullable": true,
            "description": "Who confirmed the batch (a dashboard user id, or null for an API-key confirm)."
          },
          "reviewedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "snapshotVersion": {
            "type": "integer",
            "description": "Optimistic-concurrency token. Bumps every time the batch's membership changes (a record joins, a record is excluded, a confirm lands). POST .../confirm must echo the current value or is refused 409 BATCH_STALE."
          },
          "snapshotHash": {
            "type": "string",
            "nullable": true,
            "description": "Hash of the batch's record set at the last freeze/confirm."
          },
          "records": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ReportingBatchRecord"
            },
            "description": "Every current member of the batch, plus every record that was EXCLUDED from it (still listed here, with aeatOutcome.estado = EXCLUDED and `excludedBy` set, so the batch's own history is self-consistent; the moved record appears as an ordinary member of its destination batch). Oldest first."
          },
          "pendingCommunications": {
            "type": "array",
            "description": "In-flight A1 (amend) / BAJA (cancel) ledger communications attached to this batch that are still awaiting an AEAT answer (state PENDING) — exactly the set POST /v1/reporting-batches/{id}/exclude will accept for exclusion by `communicationId`. A0 (the record's own submission) is not listed here (it appears as a batch record); a terminal or superseded communication is neither in-flight nor excludable. Oldest first.",
            "items": {
              "type": "object",
              "properties": {
                "communicationId": {
                  "type": "string",
                  "format": "uuid",
                  "description": "Pass as `communicationId` to POST .../exclude to remove this pending amend/cancel from the batch."
                },
                "operation": {
                  "type": "string",
                  "enum": [
                    "A1",
                    "BAJA"
                  ]
                },
                "operationLabel": {
                  "type": "string",
                  "description": "Human label ('Amendment' / 'Cancellation')."
                },
                "recordId": {
                  "type": "string",
                  "description": "The invoice record (GET /v1/invoices/{id}) this communication amends/cancels."
                },
                "invoiceNumber": {
                  "type": "string",
                  "nullable": true
                },
                "ejercicio": {
                  "type": "string",
                  "nullable": true,
                  "description": "AEAT exercise (year) the communication targets."
                },
                "periodo": {
                  "type": "string",
                  "nullable": true,
                  "description": "AEAT period (MM) the communication targets."
                },
                "createdAt": {
                  "type": "string",
                  "format": "date-time",
                  "nullable": true
                }
              }
            }
          }
        }
      },
      "ReportingBatchRecord": {
        "type": "object",
        "description": "One record (a mandate transaction / SII registro) as seen from a specific batch.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Mandate transaction id — the `transactionId` to pass to POST .../exclude, and the id for GET /v1/mandate-transactions/{id}."
          },
          "recordId": {
            "type": "string",
            "nullable": true,
            "description": "The invoice record id (GET /v1/invoices/{id}); null if the invoice record is not (yet) linked."
          },
          "invoiceNumber": {
            "type": "string",
            "nullable": true
          },
          "counterpartyNif": {
            "type": "string",
            "nullable": true,
            "description": "Customer (issued book) / supplier (received book) NIF."
          },
          "tipo": {
            "type": "string",
            "nullable": true,
            "description": "AEAT TipoFactura (F1, F2, R1–R5, …). Labels in vocabulary.tipoFactura."
          },
          "tipoLabel": {
            "type": "string",
            "nullable": true
          },
          "book": {
            "type": "string",
            "nullable": true,
            "enum": [
              "issued",
              "received"
            ]
          },
          "base": {
            "type": "number",
            "nullable": true,
            "description": "Base imponible (net)."
          },
          "cuota": {
            "type": "number",
            "nullable": true,
            "description": "Cuota repercutida (tax)."
          },
          "classification": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "nullable": true,
                "description": "ClaveRegimenEspecialOTrascendencia ('01'..'17'). Labels in vocabulary.claveRegimen[book]."
              },
              "label": {
                "type": "string",
                "nullable": true
              }
            }
          },
          "warnings": {
            "type": "array",
            "description": "Non-blocking validation warnings recorded against the invoice at ingestion.",
            "items": {
              "type": "object",
              "properties": {
                "ruleCode": {
                  "type": "string"
                },
                "fieldPath": {
                  "type": "string"
                },
                "errorCode": {
                  "type": "string"
                },
                "suggestedAction": {
                  "type": "string"
                },
                "checkFamily": {
                  "type": "string"
                },
                "legalBasisTag": {
                  "type": "string"
                },
                "fixUrl": {
                  "type": "string",
                  "nullable": true
                },
                "createdAt": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            }
          },
          "reportBy": {
            "type": "string",
            "format": "date",
            "nullable": true,
            "description": "This record's own AEAT report-by date."
          },
          "isLate": {
            "type": "boolean",
            "description": "This record's own reportBy was already behind today when it joined the batch (a late arrival always joins the current OPEN batch)."
          },
          "isRectification": {
            "type": "boolean"
          },
          "overdueLabel": {
            "type": "string",
            "nullable": true,
            "description": "Server-computed \"N Spanish business day(s) overdue\" for this record's own reportBy; null when not overdue."
          },
          "aeatOutcome": {
            "type": "object",
            "description": "The record's state as seen from THIS batch. `estado` is the AEAT per-record estado once submitted — PENDING before submission — or the pseudo-state EXCLUDED for a record moved out of this batch before submission (its later AEAT outcome is the destination batch's to report). Labels in vocabulary.recordStates.",
            "properties": {
              "estado": {
                "type": "string",
                "enum": [
                  "PENDING",
                  "Correcto",
                  "AceptadoConErrores",
                  "Incorrecto",
                  "EXCLUDED"
                ]
              },
              "code": {
                "type": "string",
                "nullable": true,
                "description": "AEAT error code — the rejection code on Incorrecto, the first admissible-error code on AceptadoConErrores; null otherwise."
              },
              "text": {
                "type": "string",
                "nullable": true,
                "description": "Plain-English summary: the AEAT code's meaning, the CSV on Correcto, or where an excluded record moved to."
              }
            }
          },
          "excludedBy": {
            "type": "object",
            "nullable": true,
            "description": "Set only when aeatOutcome.estado is EXCLUDED.",
            "properties": {
              "reason": {
                "type": "string"
              },
              "actor": {
                "type": "string",
                "nullable": true,
                "description": "Who excluded it — null for an API-key exclude."
              },
              "at": {
                "type": "string",
                "format": "date-time"
              },
              "movedToPeriodId": {
                "type": "string",
                "format": "uuid",
                "nullable": true,
                "description": "The batch the record now belongs to."
              }
            }
          }
        }
      },
      "ReportingBatchVocabulary": {
        "type": "object",
        "description": "Every label a caller needs to render a batch or its records without hardcoding enum text — returned on both list and detail.",
        "properties": {
          "periodStatuses": {
            "type": "object",
            "description": "status → { label, description } for open/closed/ready_for_review/submitted/filed.",
            "additionalProperties": {
              "type": "object",
              "properties": {
                "label": {
                  "type": "string"
                },
                "description": {
                  "type": "string"
                }
              }
            }
          },
          "recordStates": {
            "type": "object",
            "description": "aeatOutcome.estado → { label, description } for PENDING/Correcto/AceptadoConErrores/Incorrecto/EXCLUDED.",
            "additionalProperties": {
              "type": "object",
              "properties": {
                "label": {
                  "type": "string"
                },
                "description": {
                  "type": "string"
                }
              }
            }
          },
          "submissionModes": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string",
                  "enum": [
                    "batch_auto",
                    "batch_review"
                  ]
                },
                "label": {
                  "type": "string"
                }
              }
            }
          },
          "books": {
            "type": "object",
            "description": "book → label.",
            "additionalProperties": {
              "type": "string"
            }
          },
          "tipoFactura": {
            "type": "object",
            "description": "AEAT TipoFactura code → label.",
            "additionalProperties": {
              "type": "string"
            }
          },
          "claveRegimen": {
            "type": "object",
            "description": "{ issued: { code → label }, received: { code → label } } — ClaveRegimenEspecialOTrascendencia catalogues per book.",
            "properties": {
              "issued": {
                "type": "object",
                "additionalProperties": {
                  "type": "string"
                }
              },
              "received": {
                "type": "object",
                "additionalProperties": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "SiiAuthorityView": {
        "type": "object",
        "description": "Spain SII block 3 — one AEAT Consulta reconciliation run's counters, restructured under a name that reads as authority-observed fact.",
        "required": [
          "ranAt",
          "tier",
          "aeatCount",
          "localCount",
          "adopted",
          "missingAtAeat",
          "mismatchedEstado",
          "pageCount",
          "error"
        ],
        "properties": {
          "ranAt": {
            "type": "string",
            "format": "date-time"
          },
          "tier": {
            "type": "string",
            "description": "The sandbox tier this run executed under — mock/prewww1/prod."
          },
          "aeatCount": {
            "type": "integer",
            "description": "Count of registros AEAT's own Consulta returned for this (book, ejercicio, periodo)."
          },
          "localCount": {
            "type": "integer",
            "description": "Count of this platform's own locally-registered registros for the same scope."
          },
          "adopted": {
            "type": "integer",
            "description": "Count where AEAT's own state (including an Anulada observed via Consulta) was adopted locally."
          },
          "missingAtAeat": {
            "type": "integer",
            "description": "Count registered locally as accepted but absent from AEAT's own Consulta response — a platform-fault alert condition, never silently ignored."
          },
          "mismatchedEstado": {
            "type": "integer",
            "description": "Count where AEAT's own estado disagrees with what this platform has stored."
          },
          "pageCount": {
            "type": "integer",
            "description": "Number of Consulta result pages the sweep paged through."
          },
          "error": {
            "type": "string",
            "nullable": true,
            "description": "Non-null only when the run itself failed partway through."
          }
        }
      },
      "SiiReconciliationRun": {
        "type": "object",
        "description": "SiiAuthorityView's fields, plus the run's own identity and which (book, ejercicio, periodo) it covers.",
        "allOf": [
          {
            "type": "object",
            "required": [
              "id",
              "entityId",
              "book",
              "ejercicio",
              "periodo"
            ],
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "entityId": {
                "type": "string"
              },
              "book": {
                "type": "string",
                "enum": [
                  "issued",
                  "received"
                ]
              },
              "ejercicio": {
                "type": "string",
                "description": "4-digit year."
              },
              "periodo": {
                "type": "string",
                "description": "2-digit month."
              }
            }
          },
          {
            "$ref": "#/components/schemas/SiiAuthorityView"
          }
        ]
      },
      "RegistrationRequiredError": {
        "type": "object",
        "description": "400 body when an enabling PATCH /tax/reporting-obligations names a regime whose country the entity holds no current STANDARD tax registration for. Distinct from the flat Error shape: carries enough to deep-link the user straight to the fix. Disabling is never gated.",
        "required": [
          "ok",
          "code",
          "message",
          "country",
          "entityId",
          "fixUrl"
        ],
        "properties": {
          "ok": {
            "type": "boolean",
            "enum": [
              false
            ]
          },
          "code": {
            "type": "string",
            "enum": [
              "REGISTRATION_REQUIRED"
            ]
          },
          "message": {
            "type": "string"
          },
          "country": {
            "type": "string",
            "description": "ISO 3166-1 alpha-2 — the regime's own country.",
            "example": "ES"
          },
          "entityId": {
            "type": "string",
            "format": "uuid"
          },
          "fixUrl": {
            "type": "string",
            "format": "uri",
            "description": "Absolute, tenant-aware URL to the Registrations page pre-filtered to `country`, returning to the Jurisdictions page (\"/registrations?country=ES&returnTo=%2Fjurisdictions\" on the calling host)."
          }
        }
      },
      "ObligationHasPendingBatchError": {
        "type": "object",
        "description": "409 body when a PATCH disables es_sii while the entity still has a reporting batch that needs attention (open, ready_for_review, or overdue). Pass `force: true` to disable anyway — the batch is left completely untouched: it still carries a real AEAT deadline and a ready_for_review batch remains confirmable.",
        "required": [
          "ok",
          "code",
          "error",
          "message",
          "batchIds",
          "reportByDates",
          "fixUrl"
        ],
        "properties": {
          "ok": {
            "type": "boolean",
            "enum": [
              false
            ]
          },
          "code": {
            "type": "string",
            "enum": [
              "OBLIGATION_HAS_PENDING_BATCH"
            ]
          },
          "error": {
            "type": "string",
            "description": "Same text as message."
          },
          "message": {
            "type": "string"
          },
          "batchIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The pending batches' UUIDs (GET /v1/reporting-batches/{id})."
          },
          "reportByDates": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "date",
              "nullable": true
            },
            "description": "Each batch's report-by date, same order as batchIds."
          },
          "fixUrl": {
            "type": "string",
            "format": "uri",
            "description": "Absolute, tenant-aware URL to the Spain SII reporting page's batches tab."
          }
        }
      },
      "ReportingObligationPatch": {
        "description": "One value in PATCH /tax/reporting-obligations' `obligations` map. A bare boolean is shorthand for { enabled } and only ever succeeds for a disable — enabling requires the object form with effectiveFrom.",
        "oneOf": [
          {
            "type": "boolean"
          },
          {
            "type": "object",
            "required": [
              "enabled"
            ],
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "effectiveFrom": {
                "type": "string",
                "format": "date",
                "description": "Required when enabled is true (EFFECTIVE_FROM_REQUIRED otherwise). Omit on a disable to keep the stored value."
              },
              "submissionMode": {
                "type": "string",
                "enum": [
                  "immediate",
                  "batch_auto",
                  "batch_review"
                ],
                "description": "Must be one the regime allows — SUBMISSION_MODE_NOT_ALLOWED otherwise (es_sii allows all three, fr_ereporting allows `batch_review` and `batch_auto`, e-invoicing regimes are immediate-only). Omit to keep the stored value; a first enable with no mode starts on the regime's defaultSubmissionMode (batch_review for es_sii/fr_ereporting: nothing is filed until you confirm the batch). A change never affects a batch that is already open."
              },
              "vatRegime": {
                "type": "string",
                "enum": [
                  "reel_normal_mensuel",
                  "reel_normal_trimestriel",
                  "reel_simplifie",
                  "franchise_en_base"
                ],
                "description": "fr_ereporting only — the entity's own French VAT filing regime. Required to enable fr_ereporting (EFFECTIVE_FROM_REQUIRED-style validation applies here too). Also settable in the dashboard: entity's Jurisdictions page, France row, VAT filing regime."
              },
              "setupStatus": {
                "type": "string",
                "enum": [
                  "not_started",
                  "awaiting_customer",
                  "awaiting_authority",
                  "ready",
                  "blocked"
                ],
                "description": "Ops-only (x-admin-key) — SETUP_STATUS_FORBIDDEN for a customer key."
              },
              "setupStatusNote": {
                "type": "string",
                "description": "Ops-only (x-admin-key) — SETUP_STATUS_FORBIDDEN for a customer key."
              }
            }
          }
        ]
      },
      "QueryToolFilter": {
        "type": "object",
        "required": [
          "field",
          "operator"
        ],
        "properties": {
          "field": {
            "type": "string",
            "example": "clearanceStatus",
            "description": "A field named in this dataset's schema (see GET /query/fields)."
          },
          "operator": {
            "type": "string",
            "enum": [
              "eq",
              "ne",
              "gt",
              "lt",
              "in",
              "between",
              "contains",
              "isNull"
            ]
          },
          "value": {
            "description": "Required for eq/ne/gt/lt/contains/isNull. For isNull, a boolean (true = IS NULL, false = IS NOT NULL)."
          },
          "values": {
            "type": "array",
            "items": {},
            "description": "Required for `in` (list of values) and `between` (exactly two values: [min, max])."
          }
        }
      },
      "QueryToolValidationError": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "enum": [
              "INVALID_FILTER",
              "COST_REJECTED",
              "RATE_LIMITED",
              "FORBIDDEN"
            ]
          },
          "message": {
            "type": "string",
            "description": "Plain-English, actionable guidance — always says what to change."
          },
          "field": {
            "type": "string",
            "nullable": true,
            "description": "The filter/column/param name this error is about, when applicable."
          }
        }
      },
      "ApNotAvailable": {
        "type": "object",
        "description": "Purchases (AP) is an Enterprise-only sub-solution of Tax Calculations. `ap_not_included`: the organisation's plan does not include it (`upgradeUrl` links to the plan page). `ap_not_enabled`: the plan includes it but the business has not switched Purchases (AP) on (`hint` says where). Returned for purchase-direction calculations, the purchase_order stage, the direction=purchase and documentStage=purchase_order list filters, and writes to the AP-only supplier fields (`trusted`, `toleranceOverride`) and AP settings (`apTolerance`, `apPoDisposition`). Sales calculations are never affected.",
        "required": ["error", "message"],
        "properties": {
          "error": { "type": "string", "enum": ["ap_not_included", "ap_not_enabled"] },
          "message": { "type": "string" },
          "upgradeUrl": { "type": "string", "description": "ap_not_included only: the plan page." },
          "hint": { "type": "string", "description": "ap_not_enabled only: where to switch Purchases (AP) on." }
        }
      },
      "QueryToolErrorResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "example": false
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/QueryToolValidationError"
            }
          }
        }
      },
      "QueryToolThrottleErrorResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "example": false
          },
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "example": "RATE_LIMITED"
              },
              "message": {
                "type": "string"
              },
              "retryable": {
                "type": "boolean",
                "example": true
              }
            }
          }
        }
      },
      "QueryToolFieldDefinition": {
        "type": "object",
        "properties": {
          "field": {
            "type": "string",
            "example": "totalNet"
          },
          "type": {
            "type": "string",
            "enum": [
              "string",
              "uuid",
              "number",
              "boolean",
              "date",
              "timestamp",
              "enum"
            ]
          },
          "operators": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Empty for a computed field — never filterable."
          },
          "enumValues": {
            "type": "array",
            "nullable": true,
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                }
              }
            }
          },
          "indexed": {
            "type": "boolean",
            "description": "True only when the backing column has a real Postgres index."
          },
          "computed": {
            "type": "boolean",
            "description": "True for a derived, never-filterable, DTO-only field (e.g. effectiveRate)."
          },
          "sortable": {
            "type": "boolean",
            "description": "True only when this field may be named in a query's `sort` parameter — indexed AND non-nullable (a nullable sort column would break keyset-cursor pagination)."
          },
          "currencySemantics": {
            "type": "string",
            "nullable": true,
            "enum": [
              "original",
              "base"
            ],
            "description": "Present only on monetary-amount fields. 'original' = the row's own transaction currency."
          }
        }
      },
      "QueryToolDatasetSchema": {
        "type": "object",
        "properties": {
          "dataset": {
            "type": "string",
            "enum": [
              "einvoicing_records",
              "tax_calculations",
              "tax_calculation_line_items"
            ]
          },
          "label": {
            "type": "string",
            "example": "E-invoices",
            "description": "Customer-facing display name."
          },
          "timestampField": {
            "type": "string",
            "example": "submittedAt",
            "description": "This dataset's canonical timestamp field for from/to range filters and cursor pagination."
          },
          "limits": {
            "type": "object",
            "properties": {
              "defaultPageSize": {
                "type": "integer",
                "example": 25
              },
              "maxPageSize": {
                "type": "integer",
                "example": 100
              },
              "maxSpanDays": {
                "type": "integer",
                "example": 366,
                "description": "Maximum inclusive from/to span in days accepted in a single request."
              }
            }
          },
          "fields": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/QueryToolFieldDefinition"
            }
          },
          "defaultColumns": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "dateSemantics": {
            "type": "object",
            "properties": {
              "field": {
                "type": "string"
              },
              "timezone": {
                "type": "string",
                "example": "UTC"
              },
              "description": {
                "type": "string"
              }
            }
          }
        }
      },
      "QueryToolSchemaResponse": {
        "type": "object",
        "properties": {
          "schemaVersion": {
            "type": "string",
            "example": "1.0.0",
            "description": "Bumped only on a breaking change (a field renamed/removed, an operator's meaning changed) — never on an additive change."
          },
          "datasets": {
            "type": "object",
            "properties": {
              "einvoicing_records": {
                "$ref": "#/components/schemas/QueryToolDatasetSchema"
              },
              "tax_calculations": {
                "$ref": "#/components/schemas/QueryToolDatasetSchema"
              },
              "tax_calculation_line_items": {
                "$ref": "#/components/schemas/QueryToolDatasetSchema"
              }
            }
          }
        }
      },
      "QueryToolQueryResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "example": true
          },
          "rows": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            },
            "description": "Each row includes only the requested `columns` (or the dataset's defaultColumns), keyed by the field names from GET /query/fields."
          },
          "hasMore": {
            "type": "boolean"
          },
          "nextCursor": {
            "type": "string",
            "nullable": true,
            "description": "Opaque keyset cursor — pass back as `cursor` to fetch the next page. null when hasMore is false."
          },
          "asOf": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "RejectionDetail": {
        "type": "object",
        "description": "Structured, machine-readable explanation of a REJECTED invoice.",
        "properties": {
          "code": {
            "type": "string",
            "description": "Authority (or platform) error code, echoed verbatim from upstreamCode."
          },
          "fieldPath": {
            "type": "string",
            "nullable": true,
            "description": "Dot-path into the submitted CustomerInvoiceInput identifying the offending field, or null when the rejection isn't attributable to a single field (credentials/config problems, transmission-level errors, etc.)."
          },
          "message": {
            "type": "string",
            "description": "Plain-language explanation naming the exact field to fix."
          }
        },
        "required": [
          "code",
          "fieldPath",
          "message"
        ]
      },
      "AdjustmentAuditEntry": {
        "type": "object",
        "description": "One append-only ADJUSTMENT (auto-correction) audit row, as returned in adjustments[]. See CorrectionSummary for a reconciliation-friendly superset with a plain-English description.",
        "properties": {
          "ruleCode": {
            "type": "string"
          },
          "ruleVersion": {
            "type": "integer"
          },
          "fieldPath": {
            "type": "string"
          },
          "before": {
            "type": "string",
            "description": "The value exactly as submitted, before correction."
          },
          "after": {
            "type": "string",
            "description": "The value actually used for XML generation and submission, after correction."
          },
          "actor": {
            "type": "string",
            "description": "Who/what triggered this evaluation — an API key, a dashboard user, or an internal system channel."
          },
          "channel": {
            "type": "string"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "entityId": {
            "type": "string",
            "nullable": true
          },
          "category": {
            "type": "string",
            "description": "The firing rule's own registry category, verbatim."
          },
          "tier": {
            "type": "string",
            "enum": [
              "TIER1",
              "NORMALIZATION"
            ]
          },
          "params": {
            "type": "object",
            "nullable": true,
            "additionalProperties": true
          },
          "checkFamily": {
            "type": "string"
          },
          "legalBasisTag": {
            "type": "string",
            "enum": [
              "LEGAL_MANDATE",
              "BUSINESS_POLICY"
            ],
            "description": "Always BUSINESS_POLICY for an adjustment."
          }
        },
        "required": [
          "ruleCode",
          "ruleVersion",
          "fieldPath",
          "before",
          "after",
          "actor",
          "channel",
          "createdAt",
          "entityId",
          "category",
          "tier",
          "params",
          "checkFamily",
          "legalBasisTag"
        ]
      },
      "CorrectionSummary": {
        "type": "object",
        "description": "One entry in corrections[] — a machine-readable, reconciliation-friendly view of an applied adjustment. Same shape POST /v1/send returns synchronously in its own corrections field.",
        "properties": {
          "ruleCode": {
            "type": "string"
          },
          "ruleVersion": {
            "type": "integer"
          },
          "tier": {
            "type": "string",
            "enum": [
              "TIER1",
              "NORMALIZATION"
            ]
          },
          "fieldPath": {
            "type": "string"
          },
          "before": {
            "type": "string"
          },
          "after": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "description": "Server-generated plain-English sentence describing what changed and why."
          }
        },
        "required": [
          "ruleCode",
          "ruleVersion",
          "tier",
          "fieldPath",
          "before",
          "after",
          "description"
        ]
      },
      "WarningAuditEntry": {
        "type": "object",
        "description": "One append-only WARNING audit row, as returned in warnings[] — the same warning POST /v1/send surfaced synchronously at submission time, still readable later from this endpoint.",
        "properties": {
          "ruleCode": {
            "type": "string"
          },
          "fieldPath": {
            "type": "string"
          },
          "errorCode": {
            "type": "string"
          },
          "suggestedAction": {
            "type": "string"
          },
          "demoted": {
            "type": "boolean",
            "description": "True when this warning is actually a demoted ERROR/EXCLUDE check, not a check that's WARNING-severity by design."
          },
          "actor": {
            "type": "string"
          },
          "channel": {
            "type": "string"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "checkFamily": {
            "type": "string"
          },
          "legalBasisTag": {
            "type": "string",
            "enum": [
              "LEGAL_MANDATE",
              "BUSINESS_POLICY"
            ]
          },
          "fixUrl": {
            "type": "string",
            "nullable": true,
            "description": "Dashboard deep link for resolving this specific warning, when a genuine fix destination exists. Null when the check has no such destination."
          }
        },
        "required": [
          "ruleCode",
          "fieldPath",
          "errorCode",
          "suggestedAction",
          "demoted",
          "overridden",
          "proposedValue",
          "actor",
          "channel",
          "createdAt",
          "checkFamily",
          "legalBasisTag",
          "fixUrl"
        ]
      },
      "ExclusionAuditEntry": {
        "type": "object",
        "description": "One append-only EXCLUDE (skip) audit row, as returned in exclusions[].",
        "properties": {
          "ruleCode": {
            "type": "string"
          },
          "ruleVersion": {
            "type": "integer"
          },
          "fieldPath": {
            "type": "string"
          },
          "errorCode": {
            "type": "string"
          },
          "suggestedAction": {
            "type": "string"
          },
          "actor": {
            "type": "string"
          },
          "channel": {
            "type": "string"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "matchedList": {
            "type": "string",
            "nullable": true,
            "enum": [
              "taxId",
              "customerRef",
              "name"
            ],
            "description": "Set only when this exclusion came from a customer-exclusion rule (CUSTOMER_EXCLUSION_MATCH) match. Null for every other EXCLUDE condition type."
          },
          "matchedValue": {
            "type": "string",
            "nullable": true
          },
          "checkFamily": {
            "type": "string"
          },
          "legalBasisTag": {
            "type": "string",
            "enum": [
              "LEGAL_MANDATE",
              "BUSINESS_POLICY"
            ]
          }
        },
        "required": [
          "ruleCode",
          "ruleVersion",
          "fieldPath",
          "errorCode",
          "suggestedAction",
          "actor",
          "channel",
          "createdAt",
          "matchedList",
          "matchedValue",
          "checkFamily",
          "legalBasisTag"
        ]
      },
      "EnrichmentAuditEntry": {
        "type": "object",
        "description": "One append-only ENRICH audit row, as returned in enrichments[] — a field Clearvo's own pipeline filled in because the caller left it blank, before any validation rule ran. before is always empty by construction: an enrichment never rewrites a value the caller supplied (that is an adjustment, part of adjustments/corrections instead) — it only fills a genuine gap.",
        "properties": {
          "fieldPath": {
            "type": "string"
          },
          "before": {
            "type": "string",
            "description": "Always an empty string — see the schema description."
          },
          "after": {
            "type": "string"
          },
          "reason": {
            "type": "string",
            "description": "Machine-readable reason code identifying which enrichment step produced this row, e.g. VAT_AMOUNT_DERIVED_FROM_RATE."
          },
          "description": {
            "type": "string",
            "description": "Server-generated plain-English sentence describing what was filled in and why."
          },
          "actor": {
            "type": "string"
          },
          "channel": {
            "type": "string"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "entityId": {
            "type": "string",
            "nullable": true
          }
        },
        "required": [
          "fieldPath",
          "before",
          "after",
          "reason",
          "description",
          "actor",
          "channel",
          "createdAt",
          "entityId"
        ]
      },
      "CorrectionRef": {
        "type": "object",
        "description": "A pointer to a related invoice in the correction graph, with a ready-to-render status label — never show the raw status enum to an end user without also reading statusLabel.",
        "properties": {
          "id": {
            "type": "string"
          },
          "invoiceNumber": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "$ref": "#/components/schemas/ClearanceStatus"
          },
          "statusLabel": {
            "type": "string",
            "example": "Delivered"
          }
        },
        "required": [
          "id",
          "invoiceNumber",
          "status",
          "statusLabel"
        ]
      },
      "CorrectionRefWithRelation": {
        "description": "A CorrectionRef plus which kind of correction relationship this is.",
        "allOf": [
          {
            "$ref": "#/components/schemas/CorrectionRef"
          },
          {
            "type": "object",
            "properties": {
              "relationType": {
                "type": "string",
                "nullable": true,
                "enum": [
                  "RESUBMISSION",
                  "CREDIT_NOTE"
                ]
              }
            },
            "required": [
              "relationType"
            ]
          }
        ]
      },
      "CreditNoteRef": {
        "description": "A CorrectionRef for a credit note specifically, with its own net amount.",
        "allOf": [
          {
            "$ref": "#/components/schemas/CorrectionRef"
          },
          {
            "type": "object",
            "properties": {
              "netAmount": {
                "type": "number",
                "nullable": true
              }
            },
            "required": [
              "netAmount"
            ]
          }
        ]
      },
      "ResubmissionNumberingHint": {
        "type": "object",
        "description": "The numbering rule a Fix & Resubmit call must follow for this invoice's country — resolve this before attempting a resubmission rather than learning it only from a 422.",
        "properties": {
          "rule": {
            "type": "string",
            "enum": [
              "SAME_NUMBER",
              "NEW_NUMBER"
            ],
            "description": "SAME_NUMBER: the resubmission must reuse the original invoice number. NEW_NUMBER: the resubmission must use a fresh, unused number."
          },
          "dateMustMatch": {
            "type": "boolean",
            "description": "True when the resubmission's issueDate must exactly match the original's (Italy)."
          }
        },
        "required": [
          "rule",
          "dateMustMatch"
        ]
      },
      "ErpSourceDetail": {
        "type": "object",
        "description": "Provenance for a document synced in from a pull-based ERP integration (e.g. Zoho Books). rawPayload is the exact, unmodified JSON body the ERP returned for this document — audit-trail evidence that Clearvo submitted the ERP's own tax amounts unaltered.",
        "properties": {
          "system": {
            "type": "string",
            "example": "zoho_books"
          },
          "organizationId": {
            "type": "string",
            "nullable": true
          },
          "dcRegion": {
            "type": "string",
            "nullable": true
          },
          "resourceType": {
            "type": "string",
            "nullable": true
          },
          "externalId": {
            "type": "string",
            "nullable": true
          },
          "externalNumber": {
            "type": "string",
            "nullable": true
          },
          "rawPayload": {
            "type": "object",
            "nullable": true,
            "additionalProperties": true
          }
        },
        "required": [
          "system",
          "organizationId",
          "dcRegion",
          "resourceType",
          "externalId",
          "externalNumber",
          "rawPayload"
        ]
      },
        "RuleConditionGroup": {
          "type": "object",
          "description": "An OR or AND group: exactly one of any (at least one child matches) or all (every child matches), at least one child. Groups nest at most 2 levels deep and a rule carries at most 50 leaf conditions in total; a violation is a 422 whose details[].path names the offending condition (for example conditions[1].any[0].operator). A trace entry for a grouped rule carries matchedBranches ([{group, branch}]), the any-group branch that matched.",
          "properties": {
            "any": {
              "type": "array",
              "minItems": 1,
              "items": {
                "$ref": "#/components/schemas/RuleConditionItem"
              }
            },
            "all": {
              "type": "array",
              "minItems": 1,
              "items": {
                "$ref": "#/components/schemas/RuleConditionItem"
              }
            }
          }
        },
        "RuleConditionItem": {
          "description": "A leaf condition or a group. A flat array of leaves behaves exactly as before.",
          "oneOf": [
            {
              "$ref": "#/components/schemas/RuleCondition"
            },
            {
              "$ref": "#/components/schemas/RuleConditionGroup"
            }
          ]
        },
        "CompanyCode": {
          "type": "object",
          "required": [
            "id",
            "code",
            "name",
            "status",
            "createdAt",
            "updatedAt"
          ],
          "properties": {
            "id": {
              "type": "string",
              "format": "uuid"
            },
            "code": {
              "type": "string"
            },
            "name": {
              "type": [
                "string",
                "null"
              ]
            },
            "status": {
              "type": "string",
              "enum": [
                "active",
                "archived"
              ]
            },
            "validFrom": {
              "type": [
                "string",
                "null"
              ],
              "format": "date",
              "description": "First day (YYYY-MM-DD, inclusive) the code may be used on a transaction. null = no start limit."
            },
            "validTo": {
              "type": [
                "string",
                "null"
              ],
              "format": "date",
              "description": "Last day (YYYY-MM-DD, inclusive) the code may be used on a transaction. null = no end limit."
            },
            "createdAt": {
              "type": "string",
              "format": "date-time"
            },
            "updatedAt": {
              "type": "string",
              "format": "date-time"
            }
          }
        },
      "RuleCondition": {
        "type": "object",
        "description": "One leaf condition. A rule's conditions array is ANDed at the top level; an element is a leaf like this or a RuleConditionGroup. An empty conditions array always matches.",
        "required": [
          "property",
          "operator"
        ],
        "properties": {
          "property": {
            "type": "string",
            "description": "A property from GET /rules-engine/schema's condition/action catalog, e.g. \"customerCountry\", \"glAccount\" (built-in properties are flat) or \"customProperties.businessUnit\" (a custom field of your own)."
          },
          "operator": {
            "type": "string",
            "description": "One of GET /rules-engine/schema's allowed operators for this property, e.g. \"eq\", \"in\", \"gt\"."
          },
          "value": {}
        }
      },
      "RuleAction": {
        "type": "object",
        "description": "One action in a rule's actions array — either a literal `value` or a closed function dispatch via `fn`/`args`. Mutually exclusive.",
        "required": [
          "property"
        ],
        "properties": {
          "property": {
            "type": "string",
            "description": "A writable property from GET /rules-engine/schema's catalog."
          },
          "value": {
            "description": "A literal value to write. Mutually exclusive with fn."
          },
          "fn": {
            "type": "string",
            "description": "A closed function name from GET /rules-engine/schema's fns — mutually exclusive with value."
          },
          "args": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "Rule": {
        "type": "object",
        "description": "A rules-engine rule — the shape returned by every rule read/mutation (GET/POST/PATCH .../rules[/{id}], activate, move).",
        "properties": {
          "companyCode": {
            "type": [
              "string",
              "null"
            ],
            "description": "The company code this rule is scoped to (Enterprise), or null for an entity-wide rule. A company-code rule fires only for requests carrying that code and outranks the entity-wide rule with the same code."
          },
          "id": {
            "type": "string"
          },
          "tenantId": {
            "type": "string",
            "nullable": true
          },
          "organisationId": {
            "type": "string",
            "nullable": true
          },
          "entityId": {
            "type": "string",
            "nullable": true
          },
          "domain": {
            "type": "string",
            "enum": [
              "ap",
              "ar"
            ],
            "nullable": true,
            "description": "Deprecated (RE-9, domain unification) — no longer a structural candidacy axis. Null for any rule authored via the new domain-unification flow; a legacy value is translated server-side into an ordinary transactionDirection condition (ap→purchase, ar→sale). A new client should read/send that condition directly and ignore this field."
          },
          "recordType": {
            "type": "string",
            "enum": [
              "calculation",
              "purchase_order",
              "invoice"
            ]
          },
          "ruleKind": {
            "type": "string",
            "enum": [
              "NORMALIZATION",
              "ENRICHMENT",
              "ADJUSTMENT",
              "WARNING",
              "ERROR"
            ]
          },
          "checkpoint": {
            "type": "string",
            "enum": [
              "PRE_CALC",
              "POST_RATE_RESOLUTION"
            ],
            "default": "PRE_CALC",
            "description": "Which pipeline checkpoint this rule attaches to. PRE_CALC (default) runs once, early, before jurisdiction/classification/rate resolution — today's only behavior for every pre-existing rule. POST_RATE_RESOLUTION runs immediately after the rate is resolved and may override it (ADJUSTMENT rule kind only)."
          },
          "code": {
            "type": "string",
            "description": "Unique per (domain, recordType, scope). Letters, numbers, underscore, dot, hyphen only."
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "DRAFT",
              "ACTIVE",
              "ARCHIVED",
              "PURGED"
            ],
            "description": "DRAFT on create. Reaches ACTIVE via POST .../activate, or implicitly via PATCH { enabled: true } on a DRAFT rule (see UpdateRuleInput). ARCHIVED and PURGED are both one-way terminal retirements reached only via PATCH { action: 'delete' } — the server chooses between them based on whether the rule ever matched a real record; there is no way to set either directly, and no other DELETE exists. PURGED never appears in a GET/list response again (this value is only ever seen transiently, in the PATCH response that caused it)."
          },
          "enabled": {
            "type": "boolean",
            "description": "The day-to-day on/off switch — an ACTIVE rule with enabled=false never fires."
          },
          "legalBasisTag": {
            "type": "string",
            "enum": [
              "LEGAL_MANDATE",
              "BUSINESS_POLICY"
            ]
          },
          "sortOrder": {
            "type": "integer",
            "description": "Evaluation order is ruleKind, then sortOrder, then code."
          },
          "conditions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RuleConditionItem"
            }
          },
          "actions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RuleAction"
            }
          },
          "templateId": {
            "type": "string",
            "nullable": true
          },
          "isSystem": {
            "type": "boolean"
          },
          "lockOverride": {
            "type": "boolean",
            "nullable": true,
            "description": "For a platform default rule (isSystem true): the lock setting chosen by the content team. true = locked, false = unlocked, null = the default (a rule with a legal-mandate or platform-invariant legal basis is locked). A locked rule cannot be disabled, archived or suppressed, and its conditions cannot be changed. Customers cannot suppress a rule with a legal-mandate or platform-invariant legal basis whether or not it is unlocked."
          },
          "version": {
            "type": "integer",
            "description": "Optimistic-concurrency token — required on every PATCH."
          },
          "effectiveFrom": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "effectiveTo": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "documentUrl": {
            "type": "string",
            "nullable": true,
            "description": "A citation link (legal basis, internal policy doc, ticket) — never required."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CreateRuleInput": {
        "type": "object",
        "required": [
          "ruleKind",
          "code",
          "name"
        ],
        "properties": {
          "companyCode": {
            "type": "string",
            "description": "Scope the rule to one of the entity's ACTIVE company codes: it fires only for requests carrying that code and outranks an entity-wide rule with the same code. Entity scope only (422 company_code_requires_entity for an organisation-scoped caller), 422 company_code_unknown if the code is not an active company code of the entity, 422 company_code_not_allowed for a CONFIGURATION rule, 403 company_codes_not_included outside the Enterprise plan. Omit for an entity-wide rule. Any rule can also condition on the request's code through the read-only companyCode property."
          },
          "domain": {
            "type": "string",
            "enum": [
              "ap",
              "ar"
            ],
            "description": "Deprecated (RE-9, domain unification) — no longer required, and no longer a structural candidacy axis. Accepted for backward compatibility and translated server-side into an ordinary transactionDirection condition (ap→purchase, ar→sale); the response's `deprecations[]` names this when it happens. A new client should send a transactionDirection condition directly instead and omit this field entirely."
          },
          "recordType": {
            "type": "string",
            "enum": [
              "calculation",
              "purchase_order",
              "invoice"
            ],
            "default": "calculation"
          },
          "ruleKind": {
            "type": "string",
            "enum": [
              "NORMALIZATION",
              "ENRICHMENT",
              "ADJUSTMENT",
              "WARNING",
              "ERROR"
            ]
          },
          "checkpoint": {
            "type": "string",
            "enum": [
              "PRE_CALC",
              "POST_RATE_RESOLUTION"
            ],
            "default": "PRE_CALC",
            "description": "Which pipeline checkpoint this rule attaches to. PRE_CALC (default) runs once, early, before jurisdiction/classification/rate resolution — today's only behavior for every pre-existing rule. POST_RATE_RESOLUTION runs immediately after the rate is resolved and may override it (ADJUSTMENT rule kind only)."
          },
          "code": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "legalBasisTag": {
            "type": "string",
            "enum": [
              "LEGAL_MANDATE",
              "BUSINESS_POLICY"
            ],
            "default": "BUSINESS_POLICY"
          },
          "sortOrder": {
            "type": "integer"
          },
          "conditions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RuleConditionItem"
            }
          },
          "actions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RuleAction"
            }
          },
          "templateId": {
            "type": "string"
          },
          "effectiveFrom": {
            "type": "string",
            "format": "date-time"
          },
          "effectiveTo": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "UpdateRuleInput": {
        "type": "object",
        "required": [
          "version"
        ],
        "description": "Any subset of a rule's editable fields. `status` may only ever be set to ARCHIVED here — reaching ACTIVE or PURGED is never a literal status value you set directly (see `enabled` and `action` below). `status` and `action` are mutually exclusive — send one or the other, not both.",
        "properties": {
          "companyCode": {
            "type": [
              "string",
              "null"
            ],
            "description": "Re-scope the rule to another active company code of the entity; null makes it entity-wide again; omit to leave it unchanged. Same errors as on create."
          },
          "version": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "legalBasisTag": {
            "type": "string",
            "enum": [
              "LEGAL_MANDATE",
              "BUSINESS_POLICY"
            ]
          },
          "sortOrder": {
            "type": "integer"
          },
          "checkpoint": {
            "type": "string",
            "enum": [
              "PRE_CALC",
              "POST_RATE_RESOLUTION"
            ],
            "default": "PRE_CALC",
            "description": "Which pipeline checkpoint this rule attaches to. PRE_CALC (default) runs once, early, before jurisdiction/classification/rate resolution — today's only behavior for every pre-existing rule. POST_RATE_RESOLUTION runs immediately after the rate is resolved and may override it (ADJUSTMENT rule kind only)."
          },
          "conditions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RuleConditionItem"
            }
          },
          "actions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RuleAction"
            }
          },
          "effectiveFrom": {
            "type": "string",
            "format": "date-time"
          },
          "effectiveTo": {
            "type": "string",
            "format": "date-time"
          },
          "enabled": {
            "type": "boolean",
            "description": "The day-to-day on/off switch. `enabled: true` sent while the rule is currently DRAFT also activates it (the same transition POST .../activate performs) — you never need to check a rule's current status first to know which call to make. `enabled: true` on an already-ACTIVE rule, or `enabled: false` in any state, behaves as a plain toggle."
          },
          "status": {
            "type": "string",
            "enum": [
              "ARCHIVED"
            ]
          },
          "action": {
            "type": "string",
            "enum": [
              "delete"
            ],
            "description": "Retire this rule. You never choose ARCHIVED vs. PURGED yourself — the server resolves it based on whether the rule has ever actually matched a real record, and returns whichever happened as the response `rule.status`: ARCHIVED (kept, for audit purposes) if it has, PURGED (a genuine, invisible removal — never returned by any read call again) if it never fired. Mutually exclusive with `status`."
          }
        }
      },
      "DefaultRuleItem": {
        "type": "object",
        "description": "One platform-default row as returned by GET /rules-engine/rules?includeDefaults=1's `defaults[]` — a Clearvo-authored rules_engine rule or a legal-mandate validation_rules check, projected onto a common, RuleListItem-shaped DTO so a client can render either identically.",
        "properties": {
          "id": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "source": {
            "type": "string",
            "enum": [
              "rules_engine",
              "validation_rules"
            ],
            "description": "Which system authored this default. A client renders the two sources identically (same DTO shape) but never conflates their write paths: a rules_engine row can be suppressed via this engine's own suppress endpoint; a validation_rules row never can (readOnly is always true for it)."
          },
          "ruleKind": {
            "type": "string"
          },
          "checkpoint": {
            "type": "string",
            "enum": [
              "PRE_CALC",
              "POST_RATE_RESOLUTION"
            ],
            "default": "PRE_CALC",
            "description": "Which pipeline checkpoint this rule attaches to. PRE_CALC (default) runs once, early, before jurisdiction/classification/rate resolution — today's only behavior for every pre-existing rule. POST_RATE_RESOLUTION runs immediately after the rate is resolved and may override it (ADJUSTMENT rule kind only)."
          },
          "legalBasisTag": {
            "type": "string",
            "enum": [
              "LEGAL_MANDATE",
              "BUSINESS_POLICY"
            ]
          },
          "scopeLabel": {
            "type": "string",
            "description": "Human-readable scope label — always the Global-scope label, since every default row is Global."
          },
          "readOnly": {
            "type": "boolean",
            "description": "Always true for a validation_rules row (a separate, older system with no suppression/edit path from here at all); true for a rules_engine row only when its legalBasisTag is LEGAL_MANDATE or PLATFORM_INVARIANT — nothing here can ever weaken either."
          },
          "canSuppress": {
            "type": "boolean",
            "description": "Whether the CALLER's own scope may suppress this rule. Only ever true for a platform default (is_system) row that is not LEGAL_MANDATE — a customer's own rule is disabled directly (PATCH enabled:false) instead of suppressed."
          },
          "suppressed": {
            "type": "boolean",
            "description": "Effectively suppressed for the caller right now (ancestor-inclusive) — not just whether this exact scope suppressed it."
          },
          "suppressedByAncestor": {
            "type": "boolean",
            "description": "True when `suppressed` is true but NOT via the caller's own exact scope — a wider scope (this entity's organisation, or its tenant) suppressed it instead. The caller cannot toggle this off directly (`canSuppress` is also false whenever this is true); they'd need to act from that wider scope."
          },
          "cannotDisableReason": {
            "type": "string",
            "nullable": true,
            "description": "A plain-English reason a Switch must render as a lock instead of a toggle — null for every ordinary, freely-toggleable/suppressible row."
          },
          "status": {
            "type": "string",
            "enum": [
              "DRAFT",
              "ACTIVE",
              "ARCHIVED"
            ],
            "description": "A validation_rules row has no rules-engine status concept at all — it's a standing legal-mandate check, always ACTIVE, so this is fixed rather than derived."
          },
          "enabled": {
            "type": "boolean",
            "description": "A validation_rules row is always enabled — fixed rather than derived, same reasoning as `status`."
          },
          "appliesTo": {
            "type": "object",
            "description": "Server-derived directions this default's own conditions resolve to; no transactionDirection condition (or one covering both) means every transaction.",
            "properties": {
              "directions": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "sale",
                    "purchase"
                  ]
                }
              },
              "label": {
                "type": "string"
              }
            }
          }
        }
      },
      "RuleVersion": {
        "type": "object",
        "description": "One rules_versions snapshot — a rule's own edit history, newest first.",
        "properties": {
          "id": {
            "type": "string"
          },
          "ruleId": {
            "type": "string"
          },
          "version": {
            "type": "integer"
          },
          "snapshot": {
            "type": "object",
            "additionalProperties": true
          },
          "changedBy": {
            "type": "string",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SimulateRuleResult": {
        "type": "object",
        "properties": {
          "matchedCount": {
            "type": "integer"
          },
          "sampleSize": {
            "type": "integer"
          },
          "sample": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "recordId": {
                  "type": "string"
                },
                "before": {
                  "type": "object",
                  "additionalProperties": true
                },
                "after": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "conflicts": {
            "type": "array",
            "description": "Another ACTIVE rule that also fires on a sampled record and writes the same property — an ordering/overwrite warning, not a hard error.",
            "items": {
              "type": "object",
              "properties": {
                "ruleId": {
                  "type": "string"
                },
                "ruleCode": {
                  "type": "string"
                },
                "property": {
                  "type": "string"
                }
              }
            }
          },
          "sampledDataset": {
            "type": "string",
            "enum": ["tax_calculations", "einvoicing_records"],
            "description": "The table the preview sample was read from. Chosen by the rule's recordType: 'calculation' samples tax calculations, 'invoice' samples e-invoice records."
          },
          "notes": {
            "type": "array",
            "items": { "type": "string" },
            "description": "Caveats when the sample cannot represent the rule, e.g. an e-invoice rule previewed with recordType 'calculation', or an e-invoice preview of a rule on facts such as dueDate that the e-invoice sample does not carry. When a note is present, matchedCount 0 does not mean the rule would not fire."
          }
        }
      },
      "RulePropertyDefinition": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "tenantId": {
            "type": "string",
            "nullable": true
          },
          "organisationId": {
            "type": "string",
            "nullable": true
          },
          "entityId": {
            "type": "string",
            "nullable": true
          },
          "property": {
            "type": "string",
            "description": "Always \"customProperties.<propertyKey>\". A custom property is only for data that is not part of the platform schema; built-in fields have flat paths and are listed by GET /rules-engine/schema."
          },
          "propertyKey": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "dataType": {
            "type": "string",
            "enum": [
              "string",
              "number",
              "boolean"
            ]
          },
          "appliesTo": {
            "type": "string",
            "enum": [
              "line",
              "header"
            ]
          },
          "isWritable": {
            "type": "boolean"
          },
          "deprecatedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "null = active. A non-null timestamp means this definition is deprecated — hidden from new rule-authoring pickers by default (see ?includeDeprecated), but any existing rule or stored value referencing it keeps evaluating exactly as before. Write-time/listing-time only, never enforced at evaluation."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "RuleTemplate": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "tenantId": {
            "type": "string",
            "nullable": true
          },
          "organisationId": {
            "type": "string",
            "nullable": true
          },
          "entityId": {
            "type": "string",
            "nullable": true
          },
          "domain": {
            "type": "string",
            "enum": [
              "ap",
              "ar"
            ]
          },
          "recordType": {
            "type": "string",
            "enum": [
              "calculation",
              "purchase_order",
              "invoice"
            ]
          },
          "templateKey": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "displayMode": {
            "type": "string",
            "enum": [
              "settings_field",
              "add_row_table",
              "raw"
            ]
          },
          "cardinality": {
            "type": "string",
            "enum": [
              "single",
              "many"
            ]
          },
          "surface": {
            "type": "string",
            "nullable": true
          },
          "params": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "key": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                },
                "type": {
                  "type": "string"
                },
                "required": {
                  "type": "boolean"
                },
                "options": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "helpText": {
                  "type": "string"
                },
                "suffix": {
                  "type": "string"
                },
                "optionLabels": {
                  "type": "object",
                  "additionalProperties": { "type": "string" },
                  "description": "Plain-English label for each allowed value: the option values of an enum, or the keys \"true\" and \"false\" for a boolean."
                }
              }
            }
          },
          "dataset": {
            "type": "object",
            "nullable": true,
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "keyColumns": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          "instances": {
            "type": "array",
            "description": "Every rule this scope already has for this template (0 for an unused \"many\" template, 0 or 1 for \"single\").",
            "items": {
              "type": "object",
              "properties": {
                "ruleId": {
                  "type": "string"
                },
                "status": {
                  "type": "string"
                },
                "enabled": {
                  "type": "boolean"
                },
                "params": {
                  "type": "object",
                  "additionalProperties": true,
                  "description": "The param values this rule was saved with, keyed by the template's paramsSchema field names (for example { \"carrier\": \"common\" })."
                },
                "version": {
                  "type": "integer"
                }
              }
            }
          },
          "isSystem": {
            "type": "boolean"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "RulesEngineDataset": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "tenantId": {
            "type": "string",
            "nullable": true
          },
          "organisationId": {
            "type": "string",
            "nullable": true
          },
          "entityId": {
            "type": "string",
            "nullable": true
          },
          "datasetKey": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "keyColumns": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "1-10 column names forming this dataset's composite key."
          },
          "rowCount": {
            "type": "integer"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "RulesEngineDatasetRow": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "datasetId": {
            "type": "string"
          },
          "rowKey": {
            "type": "string",
            "description": "The already-joined composite key string — use to delete this row."
          },
          "rowData": {
            "type": "object",
            "additionalProperties": true
          },
          "effectiveFrom": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "effectiveTo": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "source": {
            "type": "string",
            "nullable": true,
            "description": "Citation — required for a Global row."
          },
          "verified": {
            "type": "boolean"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "FieldMapping": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "tenantId": {
            "type": "string",
            "nullable": true
          },
          "organisationId": {
            "type": "string",
            "nullable": true
          },
          "entityId": {
            "type": "string",
            "nullable": true
          },
          "sourceSystem": {
            "type": "string",
            "example": "xero"
          },
          "targetShape": {
            "type": "string"
          },
          "fieldPath": {
            "type": "string"
          },
          "sourcePath": {
            "type": "string",
            "nullable": true
          },
          "transform": {
            "type": "object",
            "nullable": true,
            "properties": {
              "fn": {
                "type": "string"
              },
              "args": {
                "type": "object",
                "additionalProperties": true
              }
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "ACTIVE",
              "ARCHIVED"
            ]
          },
          "enabled": {
            "type": "boolean"
          },
          "isDefault": {
            "type": "boolean",
            "description": "false once a customer has edited it."
          },
          "sortOrder": {
            "type": "integer"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "RulesTraceEntry": {
        "type": "object",
        "properties": {
          "ruleId": {
            "type": "string"
          },
          "ruleCode": {
            "type": "string"
          },
          "ruleVersion": {
            "type": "integer"
          },
          "ruleKind": {
            "type": "string",
            "enum": [
              "NORMALIZATION",
              "ENRICHMENT",
              "ADJUSTMENT",
              "WARNING",
              "ERROR"
            ]
          },
          "ruleKindLabel": {
            "type": "string"
          },
          "scopeLevel": {
            "type": "string"
          },
          "scopeLabel": {
            "type": "string"
          },
          "property": {
            "type": "string",
            "nullable": true
          },
          "propertyLabel": {
            "type": "string",
            "nullable": true
          },
          "beforeValue": {},
          "afterValue": {},
          "phase": {
            "type": "string",
            "enum": [
              "pre_calc",
              "post_calc",
              "post_rate_resolution"
            ]
          },
          "channel": {
            "type": "string"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "sentence": {
            "type": "string",
            "description": "Backend-composed, customer-facing prose."
          },
          "shadowed": {
            "type": "boolean",
            "description": "true when a more-specific same-code rule shadowed this one and its actions did NOT apply."
          }
        }
      },
      "PlatformRuleChange": {
        "type": "object",
        "properties": {
          "ruleId": {
            "type": "string"
          },
          "version": {
            "type": "integer"
          },
          "code": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "domain": {
            "type": "string"
          },
          "recordType": {
            "type": "string",
            "enum": [
              "calculation",
              "purchase_order",
              "invoice"
            ]
          },
          "ruleKind": {
            "type": "string",
            "enum": [
              "NORMALIZATION",
              "ENRICHMENT",
              "ADJUSTMENT",
              "WARNING",
              "ERROR"
            ]
          },
          "checkpoint": {
            "type": "string",
            "enum": [
              "PRE_CALC",
              "POST_RATE_RESOLUTION"
            ],
            "default": "PRE_CALC",
            "description": "Which pipeline checkpoint this rule attaches to. PRE_CALC (default) runs once, early, before jurisdiction/classification/rate resolution — today's only behavior for every pre-existing rule. POST_RATE_RESOLUTION runs immediately after the rate is resolved and may override it (ADJUSTMENT rule kind only)."
          },
          "ruleKindLabel": {
            "type": "string"
          },
          "scopeLevel": {
            "type": "string",
            "enum": [
              "global",
              "tenant"
            ]
          },
          "scopeLabel": {
            "type": "string"
          },
          "effectiveFrom": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "documentUrl": {
            "type": "string",
            "nullable": true
          },
          "changedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "RulesEngineSchema": {
        "type": "object",
        "description": "The rules-engine's own vocabulary — call before authoring any rule. `properties[]` is the built-in catalog, addressed by flat paths (for example `glAccount`, `costCenter`, `intendedUse`, `accountAssignment`, `commodityCode`, `commodityCodeScheme` — the last two read-only). Custom fields are separate: they are for data that is not in the schema and are addressed as `customProperties.<key>` (GET /rules-engine/properties).",
        "properties": {
          "schemaVersion": {
            "type": "string",
            "example": "2.3.0"
          },
          "domains": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "recordTypes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "evaluatedRecordTypesByDomain": {
            "type": "object",
            "additionalProperties": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          "ruleKinds": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "statuses": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "operators": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "functions": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "deprecated": true,
            "description": "Use fns instead, which carries simulatesOutcome per entry."
          },
          "fns": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "fn": {
                  "type": "string"
                },
                "simulatesOutcome": {
                  "type": "boolean"
                }
              },
              "additionalProperties": true
            }
          },
          "properties": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "property": {
                  "type": "string"
                },
                "type": {
                  "type": "string"
                },
                "operators": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "writable": {
                  "type": "boolean"
                },
                "taxDeterminative": {
                  "type": "boolean"
                }
              },
              "additionalProperties": true
            }
          },
          "source": {
            "type": "object",
            "properties": {
              "values": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "labels": {
                "type": "object",
                "additionalProperties": {
                  "type": "string"
                }
              },
              "coverage": {
                "type": "object",
                "additionalProperties": {
                  "type": "string",
                  "enum": [
                    "full",
                    "partial"
                  ]
                }
              }
            }
          }
        }
      },
      "Usage": {
        "type": "object",
        "description": "Usage against the plan allowance for the current billing period.",
        "properties": {
          "plan": {
            "type": "string",
            "description": "Effective plan slug."
          },
          "billingStatus": {
            "type": "string",
            "description": "Billing status of the organisation, or 'none' when it has no subscription."
          },
          "period": {
            "type": "object",
            "nullable": true,
            "description": "Current billing period; null when the organisation has no active subscription period.",
            "properties": {
              "start": {
                "type": "string",
                "format": "date-time"
              },
              "end": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "used": {
            "type": "integer",
            "description": "Billable transactions counted so far in the period."
          },
          "included": {
            "type": "integer",
            "nullable": true,
            "description": "Transactions included in the plan; null when unlimited."
          },
          "remaining": {
            "type": "integer",
            "nullable": true,
            "description": "Allowance left; null when unlimited."
          },
          "percentUsed": {
            "type": "number",
            "nullable": true,
            "description": "Percentage of the allowance used (one decimal); null when unlimited."
          },
          "overageUsd": {
            "type": "number",
            "nullable": true,
            "description": "Per-transaction overage price in USD; null when the plan has no overage price."
          },
          "overage": {
            "type": "integer",
            "description": "Transactions beyond the allowance. Never charged while the organisation is in its trial."
          },
          "inTrial": {
            "type": "boolean"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When the hourly ledger last changed; counts can trail live traffic by up to an hour."
          }
        }
      },
      "BusinessStatusAction": {
        "type": "object",
        "description": "One next business-status action offered on a received document. Pass `status` as the target of PATCH /invoices/{id}/business-status.",
        "required": [
          "status",
          "label"
        ],
        "properties": {
          "status": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "description": "A sentence stating the action's consequence; omitted when there is nothing to add."
          },
          "requiresDetail": {
            "type": "boolean",
            "description": "True when a reason/message step is needed before the action can be fired."
          },
          "reasonOptions": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "value",
                "label"
              ],
              "properties": {
                "value": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                },
                "requiresMessage": {
                  "type": "boolean"
                }
              }
            }
          },
          "messageLabel": {
            "type": "string"
          },
          "messageDescription": {
            "type": "string"
          },
          "messageMinLength": {
            "type": "integer"
          },
          "messageMaxLength": {
            "type": "integer"
          }
        }
      },
      "TestRunStartRequest": {
        "type": "object",
        "description": "Start a run of your integration tests in the sandbox.",
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "description": "A name for the run (up to 200 characters)."
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Run only tests carrying one of these tags. Default [\"automatic\"], the tests the platform can check on its own; tests that need a manual step are reported as not run."
          },
          "caseIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Run only these tests (caseId values from GET /v1/test-cases)."
          },
          "deadlineMinutes": {
            "type": "integer",
            "description": "How long the run may wait for your integration to act, 1 to 1440 minutes. Default 10.",
            "minimum": 1,
            "maximum": 1440
          }
        }
      },
      "TestRunStarted": {
        "type": "object",
        "required": [
          "runId",
          "status",
          "links",
          "disclaimer"
        ],
        "properties": {
          "runId": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "passed",
              "failed",
              "waiting",
              "cancelled"
            ]
          },
          "startedAt": {
            "type": "string",
            "nullable": true,
            "format": "date-time"
          },
          "deadlineAt": {
            "type": "string",
            "format": "date-time"
          },
          "links": {
            "type": "object",
            "properties": {
              "self": {
                "type": "string",
                "description": "Path of GET /v1/test-runs/{id} for this run."
              }
            }
          },
          "idempotent": {
            "type": "boolean",
            "description": "true when the same x-idempotency-key already started this run; the same body is returned with 200."
          },
          "disclaimer": {
            "type": "string",
            "description": "Present on every response: how to read a result. These results show how your integration handled simulated scenarios in the sandbox on the date shown. Nothing was sent to any tax authority, network or supplier. This is not a certification, accreditation or approval by any authority, by OpenPeppol or by any e-invoicing platform, and it does not assess the tax treatment of your documents."
          }
        }
      },
      "TestRunSummary": {
        "type": "object",
        "properties": {
          "passed": {
            "type": "integer"
          },
          "failed": {
            "type": "integer"
          },
          "waiting": {
            "type": "integer"
          },
          "notRun": {
            "type": "integer"
          },
          "notApplicable": {
            "type": "integer"
          },
          "autoVerified": {
            "type": "integer"
          },
          "selfAttested": {
            "type": "integer"
          },
          "required": {
            "type": "integer"
          },
          "requiredPassed": {
            "type": "integer"
          }
        },
        "description": "Counts for the run. autoVerified counts tests the platform checked itself; selfAttested counts tests marked done by hand, which never count towards a passed run."
      },
      "TestRunCase": {
        "type": "object",
        "properties": {
          "caseId": {
            "type": "string"
          },
          "caseVersion": {
            "type": "integer",
            "description": "The version of the test this result ran against."
          },
          "title": {
            "type": "string"
          },
          "group": {
            "type": "string"
          },
          "required": {
            "type": "boolean"
          },
          "scope": {
            "type": "string",
            "description": "Where the test comes from: standard, added_for_you (added for your account) or your_organisation (you added it).",
            "enum": [
              "standard",
              "added_for_you",
              "your_organisation"
            ]
          },
          "outcome": {
            "type": "string",
            "enum": [
              "not_run",
              "passed",
              "failed",
              "waiting"
            ]
          },
          "verification": {
            "type": "string",
            "description": "auto = the platform checked it; self_attested = marked done by hand.",
            "nullable": true,
            "enum": [
              "auto",
              "self_attested"
            ]
          },
          "reasonCode": {
            "type": "string",
            "description": "Stable code for why a test failed or did not run.",
            "nullable": true
          },
          "reason": {
            "type": "string",
            "description": "One plain-English sentence for reasonCode.",
            "nullable": true
          },
          "firedAt": {
            "type": "string",
            "nullable": true,
            "format": "date-time"
          },
          "resolvedAt": {
            "type": "string",
            "nullable": true,
            "format": "date-time"
          },
          "windowEndsAt": {
            "type": "string",
            "description": "While outcome is waiting: when the window for your integration to act ends.",
            "nullable": true,
            "format": "date-time"
          },
          "evidence": {
            "type": "object",
            "additionalProperties": true,
            "description": "Ids and metadata behind the outcome (webhook, delivery and invoice ids, HTTP status, timings). Never a request or response body."
          }
        }
      },
      "TestRun": {
        "type": "object",
        "required": [
          "runId",
          "status",
          "summary",
          "cases",
          "disclaimer"
        ],
        "properties": {
          "runId": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "description": "passed only when every required test passed with the platform's own check; a test marked done by hand never counts.",
            "enum": [
              "queued",
              "running",
              "passed",
              "failed",
              "waiting",
              "cancelled"
            ]
          },
          "startedAt": {
            "type": "string",
            "nullable": true,
            "format": "date-time"
          },
          "completedAt": {
            "type": "string",
            "nullable": true,
            "format": "date-time"
          },
          "deadlineAt": {
            "type": "string",
            "format": "date-time"
          },
          "summary": {
            "$ref": "#/components/schemas/TestRunSummary"
          },
          "cases": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TestRunCase"
            }
          },
          "notApplicable": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "caseId": {
                  "type": "string"
                },
                "title": {
                  "type": "string"
                },
                "reason": {
                  "type": "string"
                }
              }
            },
            "description": "Tests that do not apply to your setup, with why."
          },
          "links": {
            "type": "object",
            "properties": {
              "self": {
                "type": "string"
              }
            }
          },
          "disclaimer": {
            "type": "string",
            "description": "Present on every response: how to read a result. These results show how your integration handled simulated scenarios in the sandbox on the date shown. Nothing was sent to any tax authority, network or supplier. This is not a certification, accreditation or approval by any authority, by OpenPeppol or by any e-invoicing platform, and it does not assess the tax treatment of your documents."
          }
        }
      },
      "TestRunList": {
        "type": "object",
        "required": [
          "runs",
          "disclaimer"
        ],
        "properties": {
          "runs": {
            "type": "array",
            "description": "Newest first, up to 50.",
            "items": {
              "type": "object",
              "properties": {
                "runId": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "queued",
                    "running",
                    "passed",
                    "failed",
                    "waiting",
                    "cancelled"
                  ]
                },
                "startedAt": {
                  "type": "string",
                  "nullable": true,
                  "format": "date-time"
                },
                "completedAt": {
                  "type": "string",
                  "nullable": true,
                  "format": "date-time"
                },
                "deadlineAt": {
                  "type": "string",
                  "format": "date-time"
                },
                "summary": {
                  "$ref": "#/components/schemas/TestRunSummary"
                },
                "links": {
                  "type": "object",
                  "properties": {
                    "self": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "disclaimer": {
            "type": "string",
            "description": "Present on every response: how to read a result. These results show how your integration handled simulated scenarios in the sandbox on the date shown. Nothing was sent to any tax authority, network or supplier. This is not a certification, accreditation or approval by any authority, by OpenPeppol or by any e-invoicing platform, and it does not assess the tax treatment of your documents."
          }
        }
      },
      "TestCase": {
        "type": "object",
        "properties": {
          "caseId": {
            "type": "string"
          },
          "caseVersion": {
            "type": "integer"
          },
          "title": {
            "type": "string"
          },
          "group": {
            "type": "string",
            "enum": [
              "sales_outbound",
              "purchase_inbound",
              "tax_calculation",
              "tax_number_validation",
              "webhooks",
              "errors_edge_cases"
            ]
          },
          "scope": {
            "type": "string",
            "enum": [
              "standard",
              "added_for_you",
              "your_organisation"
            ]
          },
          "required": {
            "type": "boolean"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "applicable": {
            "type": "boolean",
            "description": "false when the test does not apply to your setup."
          },
          "notApplicableReason": {
            "type": "string",
            "nullable": true
          },
          "verifiability": {
            "type": "string",
            "description": "auto = the platform checks it in a run; attest_only = you mark it done by hand in the dashboard.",
            "enum": [
              "auto",
              "attest_only"
            ]
          },
          "steps": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "expected": {
            "type": "string",
            "description": "What should happen when the steps are done."
          }
        }
      },
      "TestCaseList": {
        "type": "object",
        "required": [
          "cases",
          "disclaimer"
        ],
        "properties": {
          "cases": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TestCase"
            }
          },
          "disclaimer": {
            "type": "string",
            "description": "Present on every response: how to read a result. These results show how your integration handled simulated scenarios in the sandbox on the date shown. Nothing was sent to any tax authority, network or supplier. This is not a certification, accreditation or approval by any authority, by OpenPeppol or by any e-invoicing platform, and it does not assess the tax treatment of your documents."
          }
        }
      },
      "TestCaseRequest": {
        "type": "object",
        "description": "A test of your own. With a check the platform verifies it in a run; without one you mark it done by hand in the dashboard.",
        "required": [
          "title",
          "steps",
          "expected"
        ],
        "properties": {
          "title": {
            "type": "string",
            "description": "A short name for the test (up to 200 characters)."
          },
          "steps": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "What to do, one step per item (up to 20)."
          },
          "expected": {
            "type": "string",
            "description": "What should happen when the steps are done (up to 1000 characters)."
          },
          "description": {
            "type": "string",
            "description": "Optional extra detail."
          },
          "check": {
            "type": "object",
            "description": "Optional check the platform runs. kind and params come from GET /v1/test-cases/catalogue.",
            "required": [
              "kind",
              "params"
            ],
            "properties": {
              "kind": {
                "type": "string"
              },
              "params": {
                "type": "object",
                "additionalProperties": true
              }
            }
          },
          "appliesWhen": {
            "type": "object",
            "description": "Optional: limit the test to a direction and countries.",
            "properties": {
              "direction": {
                "type": "string",
                "enum": [
                  "any",
                  "sale",
                  "purchase"
                ]
              },
              "countries": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "TestCaseWritten": {
        "type": "object",
        "required": [
          "id",
          "verifiability",
          "verifiabilityLabel",
          "disclaimer"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "verifiability": {
            "type": "string",
            "enum": [
              "auto",
              "attest_only"
            ]
          },
          "verifiabilityLabel": {
            "type": "string",
            "description": "Checked automatically, or You mark it done."
          },
          "disclaimer": {
            "type": "string",
            "description": "Present on every response: how to read a result. These results show how your integration handled simulated scenarios in the sandbox on the date shown. Nothing was sent to any tax authority, network or supplier. This is not a certification, accreditation or approval by any authority, by OpenPeppol or by any e-invoicing platform, and it does not assess the tax treatment of your documents."
          }
        }
      },
      "TestCaseArchived": {
        "type": "object",
        "required": [
          "id",
          "archived"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "archived": {
            "type": "boolean",
            "enum": [
              true
            ]
          }
        }
      },
      "TestCaseCatalogue": {
        "type": "object",
        "required": [
          "checks",
          "appliesWhen",
          "limits",
          "disclaimer"
        ],
        "properties": {
          "checks": {
            "type": "array",
            "description": "One per check a test may use (one check per test).",
            "items": {
              "type": "object",
              "properties": {
                "kind": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                },
                "params": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "A setting: name, label, type (enum or integer), its options or min, max and default."
                  }
                }
              }
            }
          },
          "appliesWhen": {
            "type": "object",
            "properties": {
              "directions": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "value": {
                      "type": "string"
                    },
                    "label": {
                      "type": "string"
                    }
                  }
                }
              },
              "countries": {
                "type": "array",
                "description": "The countries your entity is registered in.",
                "items": {
                  "type": "object",
                  "properties": {
                    "value": {
                      "type": "string"
                    },
                    "label": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "limits": {
            "type": "object",
            "properties": {
              "titleMax": {
                "type": "integer"
              },
              "textMax": {
                "type": "integer"
              },
              "stepsMax": {
                "type": "integer"
              },
              "casesMax": {
                "type": "integer"
              },
              "casesUsed": {
                "type": "integer"
              }
            }
          },
          "disclaimer": {
            "type": "string",
            "description": "Present on every response: how to read a result. These results show how your integration handled simulated scenarios in the sandbox on the date shown. Nothing was sent to any tax authority, network or supplier. This is not a certification, accreditation or approval by any authority, by OpenPeppol or by any e-invoicing platform, and it does not assess the tax treatment of your documents."
          }
        }
      },
      "SimulateReceivedRequest": {
        "type": "object",
        "description": "All fields optional. With no body one invoice is created for the entity's own country.",
        "additionalProperties": false,
        "properties": {
          "country": {
            "type": "string",
            "description": "The registration the documents are received under: any country the entity is established or VAT-registered in (DE or FR). Defaults to the establishment country, else the single supported registration.",
            "enum": [
              "DE",
              "FR"
            ]
          },
          "type": {
            "type": "string",
            "default": "invoice",
            "enum": [
              "invoice",
              "credit_note"
            ]
          },
          "count": {
            "type": "integer",
            "description": "How many documents to create, 1 to 20. Each call adds new ones.",
            "minimum": 1,
            "maximum": 20,
            "default": 1
          },
          "supplier": {
            "type": "object",
            "description": "Override of the fictional supplier. The name always keeps a sample marker.",
            "properties": {
              "name": {
                "type": "string"
              },
              "taxNumber": {
                "type": "string",
                "description": "Must be valid for the country and never a real registered one."
              },
              "legalRegistrationNumber": {
                "type": "string",
                "description": "France: SIREN or SIRET."
              }
            }
          },
          "totalAmount": {
            "type": "number",
            "description": "Gross total of each document. Default 1200. Not allowed together with lines."
          },
          "currency": {
            "type": "string",
            "description": "Three-letter code. Default EUR; France accepts EUR only."
          },
          "lines": {
            "type": "array",
            "description": "Lines to put on each document; the tax rate is the standard rate. Not allowed together with totalAmount.",
            "items": {
              "type": "object",
              "properties": {
                "description": {
                  "type": "string"
                },
                "quantity": {
                  "type": "number"
                },
                "unitPrice": {
                  "type": "number",
                  "description": "Net unit price."
                }
              }
            }
          },
          "issueDate": {
            "type": "string",
            "format": "date"
          },
          "businessStatus": {
            "type": "string",
            "description": "Moves each document along its lifecycle after it arrives (for example APPROVED, DISPUTED, REFUSED), firing the matching status webhooks. A status that cannot be reached is 400 INVALID_BUSINESS_STATUS."
          }
        }
      },
      "SimulateReceivedResult": {
        "type": "object",
        "required": [
          "ok",
          "sandbox",
          "created"
        ],
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "sandbox": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "created": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InvoiceListItem"
            },
            "description": "The created received invoices, as GET /v1/invoices returns them (intakeChannel SANDBOX_SIMULATION)."
          },
          "duplicates": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Ids of documents that were already received."
          },
          "webhooks": {
            "type": "object",
            "properties": {
              "event": {
                "type": "string"
              },
              "subscribedEndpoints": {
                "type": "integer",
                "description": "How many of your webhook endpoints are subscribed to invoice.received."
              }
            }
          },
          "notes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "WebhookTestRequest": {
        "type": "object",
        "required": [
          "webhookId"
        ],
        "description": "Send a signed test event to one of your webhooks.",
        "properties": {
          "webhookId": {
            "type": "string",
            "description": "The id of a webhook you registered (from POST or GET /v1/webhooks)."
          },
          "event": {
            "type": "string",
            "default": "invoice.received",
            "enum": [
              "invoice.received",
              "invoice.status_changed",
              "invoice.accepted",
              "invoice.rejected",
              "invoice.duplicate",
              "invoice.undelivered",
              "invoice.pending",
              "invoice.business_status_changed"
            ],
            "description": "The invoice event to simulate. `invoice.status_changed` is simulated with a sample status change; choose the new status with `to`.",
            "example": "invoice.received"
          },
          "to": {
            "allOf": [{ "$ref": "#/components/schemas/ClearanceStatus" }],
            "description": "Optional, only valid with event `invoice.status_changed`: the new status the sample reports, so you can test your handler for a hold or a rejection. Defaults to the status of the invoice when `invoiceId` is given, otherwise ACCEPTED. Sent as the payload's `to` and `clearanceStatus`, with a matching `statusClass`, `reason` and `nextStep`."
          },
          "invoiceId": {
            "type": "string",
            "description": "Optional. The id of one of your own invoices (GET /v1/invoices); the payload is then built from that invoice instead of sample values."
          }
        }
      },
      "WebhookTestResult": {
        "type": "object",
        "required": [
          "webhookId",
          "event",
          "delivered",
          "status",
          "durationMs",
          "subscribed",
          "payload"
        ],
        "properties": {
          "webhookId": {
            "type": "string"
          },
          "event": {
            "type": "string",
            "example": "invoice.received"
          },
          "delivered": {
            "type": "boolean",
            "description": "True when your endpoint answered with a 2xx status."
          },
          "status": {
            "type": "integer",
            "nullable": true,
            "description": "The HTTP status your endpoint answered with, or null when no response was received."
          },
          "durationMs": {
            "type": "integer"
          },
          "subscribed": {
            "type": "boolean",
            "description": "False when the webhook is not subscribed to this event, so a real event of this type would not be sent to it. The test event is sent either way. For `invoice.status_changed` it is also false when the webhook does not list the event by name (`*` does not include it) or its `statuses` filter does not include the status in `to`."
          },
          "payload": {
            "type": "object",
            "additionalProperties": true,
            "description": "The exact JSON body that was sent, including \"test\": true and webhookId."
          },
          "error": {
            "type": "object",
            "description": "Present when delivered is false.",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "non_2xx_response",
                  "redirect_not_followed",
                  "destination_not_allowed",
                  "timeout",
                  "connection_failed"
                ]
              },
              "message": {
                "type": "string"
              }
            }
          },
          "note": {
            "type": "string",
            "description": "Present when the webhook is not subscribed to the event. It explains when a real event would not reach the hook: the event is not listed (for `invoice.status_changed`, `*` does not count) or the status is not in the webhook's `statuses`."
          }
        }
      },
      "WebhookCreateRequest": {
        "type": "object",
        "required": [
          "url"
        ],
        "description": "Register a webhook endpoint. The signing secret is generated by the server (see WebhookCreated) — it is never supplied by the client.",
        "properties": {
          "statuses": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/ClearanceStatus" },
            "description": "Only applies to the `invoice.status_changed` event: which statuses to receive, matched on the new status, for example [\"NEEDS_INFO\", \"SETUP_NEEDED\"] to be told when you must act. Omit or send an empty list for every status. An unknown status is rejected with 400, and so is `statuses` without `invoice.status_changed` in `events`."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Must be an https:// URL.",
            "example": "https://your-app.com/webhooks/clearvo"
          },
          "events": {
            "type": "array",
            "default": [
              "*"
            ],
            "description": "Events to subscribe to. Optional; defaults to [\"*\"] (every event except `invoice.status_changed`, which is never included by `*` and must be listed by name). See WebhookEndpoint.events for the accepted values — any other value is rejected with 400.",
            "items": {
              "$ref": "#/components/schemas/WebhookEndpoint/properties/events/items"
            },
            "example": [
              "invoice.accepted",
              "invoice.rejected"
            ]
          },
          "entityId": {
            "type": "string",
            "format": "uuid",
            "description": "Request (POST) only, and only meaningful for an entity-restricted organisation-scoped key: which of the key's allowed entities this webhook targets — required in that case (403 if missing or outside the allowlist), ignored otherwise. An unrestricted organisation-scoped key omits this to register an account-wide webhook (delivered for every entity), which is also this field's default absence when returned in a GET/POST response for such a webhook."
          }
        }
      },
      "WebhookCreated": {
        "description": "The 201 response: the registered webhook plus its signing secret, returned this one time only — store it securely, it cannot be retrieved again. Deliveries are signed with x-taxually-signature: sha256=HMAC-SHA256(secret, `${x-taxually-timestamp}.${body}`).",
        "allOf": [
          {
            "$ref": "#/components/schemas/WebhookEndpoint"
          },
          {
            "type": "object",
            "required": [
              "secret"
            ],
            "properties": {
              "secret": {
                "type": "string",
                "example": "whsec_0123456789abcdef0123456789abcdef0123456789abcdef",
                "description": "Server-generated signing secret (whsec_ prefix). Shown once."
              }
            }
          }
        ]
      },
      "TaxShipFromAddress": {
        "description": "Dispatch address: a TaxAddress plus customsStatus.",
        "allOf": [
          {
            "$ref": "#/components/schemas/TaxAddress"
          },
          {
            "type": "object",
            "properties": {
              "customsStatus": {
                "type": "string",
                "enum": [
                  "free_circulation",
                  "bonded"
                ],
                "description": "'bonded' means the goods are held under customs control, so leaving them is a customs crossing. Only matters when duties are requested."
              }
            }
          }
        ]
      },
      "FilingAccess": {
        "type": "object",
        "description": "Whether the account may set up filing now. Distinct from `canStart` (eligibility and bank account).",
        "properties": {
          "canSetUp": {
            "type": "boolean"
          },
          "message": {
            "type": "string",
            "nullable": true,
            "description": "The sentence to show; null when canSetUp. Equals the 402 message."
          },
          "subscribeUrl": {
            "type": "string",
            "nullable": true,
            "description": "Absolute billing page; null unless the caller can start a subscription (owner or admin in the dashboard)."
          },
          "subscribeLabel": {
            "type": "string",
            "nullable": true,
            "description": "Button text for subscribeUrl."
          }
        }
      },
      "FilingPeriodOption": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "example": "2026-Q4",
            "description": "Period key: YYYY-MM or YYYY-Qn."
          },
          "label": {
            "type": "string",
            "example": "Q4 2026"
          },
          "startsOn": {
            "type": "string",
            "format": "date"
          },
          "endsOn": {
            "type": "string",
            "format": "date"
          },
          "estimated": {
            "type": "boolean",
            "description": "True when the due dates were unavailable and the approximation was used."
          }
        }
      },
      "FilingStartOptions": {
        "type": "object",
        "properties": {
          "earliestStartLabel": {
            "type": "string",
            "example": "1 October 2026"
          },
          "earliestStartNote": {
            "type": "string"
          },
          "firstAvailable": {
            "$ref": "#/components/schemas/FilingPeriodOption"
          },
          "later": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FilingPeriodOption"
            },
            "description": "The next 8 periods after firstAvailable."
          }
        }
      },
      "FilingSetupSummary": {
        "type": "object",
        "properties": {
          "registrationId": {
            "type": "string"
          },
          "jurisdictionId": {
            "type": "string",
            "example": "US_CA"
          },
          "jurisdictionLabel": {
            "type": "string",
            "example": "California",
            "description": "The state or country name, never a code."
          },
          "registrationLabel": {
            "type": "string",
            "description": "The registration number as stored."
          },
          "state": {
            "type": "string",
            "enum": [
              "NOT_SET_UP",
              "REQUESTED",
              "ENABLED",
              "STOPPED_AFTER"
            ]
          },
          "stateLabel": {
            "type": "string",
            "example": "Waiting for onboarding"
          },
          "nextStep": {
            "type": "string",
            "enum": [
              "SET_UP",
              "ADD_PAYMENT_ACCOUNT",
              "WAIT_FOR_ONBOARDING",
              "NONE",
              "RESTART"
            ]
          },
          "nextStepLabel": {
            "type": "string",
            "nullable": true,
            "example": "Choose how often you file and when to start."
          },
          "actionLabel": {
            "type": "string",
            "nullable": true,
            "example": "Set up filing"
          },
          "frequencyLabel": {
            "type": "string",
            "nullable": true,
            "example": "Monthly"
          },
          "startPeriodLabel": {
            "type": "string",
            "nullable": true,
            "example": "December 2026"
          },
          "eligible": {
            "type": "boolean"
          },
          "ineligibleReason": {
            "type": "string",
            "nullable": true,
            "description": "Display text: why this registration is not offered for filing, or that filing is temporarily unavailable."
          }
        }
      },
      "FilingSetup": {
        "type": "object",
        "properties": {
          "filingAccess": {
            "$ref": "#/components/schemas/FilingAccess"
          },
          "registrationId": {
            "type": "string"
          },
          "eligible": {
            "type": "boolean"
          },
          "ineligibleReason": {
            "type": "string",
            "nullable": true,
            "enum": [
              "locality",
              "country"
            ],
            "description": "Why this registration cannot file: `locality` (a local registration) or `country` (not available yet). Null when eligible, and null while filing data is temporarily unavailable (see `startBlockedReason`)."
          },
          "returnType": {
            "type": "string",
            "nullable": true,
            "description": "The return this registration files. Null while it cannot be determined."
          },
          "state": {
            "type": "string",
            "enum": [
              "NOT_SET_UP",
              "REQUESTED",
              "ENABLED",
              "STOPPED_AFTER"
            ]
          },
          "frequencyCode": {
            "type": "string",
            "nullable": true
          },
          "proposedStartDate": {
            "type": "string",
            "format": "date",
            "nullable": true,
            "description": "First day of the chosen start period."
          },
          "startChoice": {
            "type": "string",
            "nullable": true,
            "enum": [
              "asap",
              "later",
              "backdated"
            ],
            "description": "The stored start choice."
          },
          "startPeriodKey": {
            "type": "string",
            "nullable": true
          },
          "startPeriodLabel": {
            "type": "string",
            "nullable": true,
            "example": "December 2026"
          },
          "frequencyOptions": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string",
                  "example": "QuarterlyPrepay"
                },
                "label": {
                  "type": "string",
                  "example": "Quarterly with prepayments"
                },
                "description": {
                  "type": "string"
                }
              }
            }
          },
          "frequencyOptionsSource": {
            "type": "string",
            "enum": [
              "live",
              "fallback"
            ],
            "description": "`fallback` while the tax authority source is unreachable: Monthly, Quarterly and Quarterly with prepayments, with estimated start periods."
          },
          "startOptionsByFrequency": {
            "type": "object",
            "additionalProperties": {
              "$ref": "#/components/schemas/FilingStartOptions"
            }
          },
          "frequencyQuestion": {
            "type": "string"
          },
          "frequencyHelp": {
            "type": "string"
          },
          "frequencyWarning": {
            "type": "string"
          },
          "frequencyAttestationLabel": {
            "type": "string"
          },
          "startQuestion": {
            "type": "string"
          },
          "canStart": {
            "type": "boolean"
          },
          "startBlockedReason": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "FilingSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Either `fil_...` or, for a live filing, `ret_<number>`."
          },
          "live": {
            "type": "boolean",
            "description": "Present and true when the filing is read live from the filing service on this request rather than stored. A live filing can be approved (`POST /v1/filings/{id}/approve`), paused and resumed (`/pause`, `/resume`): `allowedActions.approve` is true only while its status is READY_FOR_APPROVAL, the caller may change filings and any bank account the tax authority debits is on file; `allowedActions.pause` and `.resume` mirror `pause.canPause` and `pause.canResume`; every other action route answers 404 for a `ret_` id."
          },
          "reviewLabel": {
            "type": "string",
            "nullable": true,
            "example": "Review by 5 Oct. If not approved we finalise it as it is.",
            "description": "Live filings only: one plain sentence on what happens next while the return waits for approval (the review date, a pause, or just \"Review this return.\" for a backdated return); null when nothing is pending. Render it as is."
          },
          "pause": {
            "type": "object",
            "description": "Live filings only, omitted when it could not be read: what the customer may do about pausing. Every sentence is display text; render it as is.",
            "properties": {
              "canPause": {
                "type": "boolean"
              },
              "canPauseCode": {
                "type": "string",
                "nullable": true,
                "enum": [
                  "NOT_AWAITING_APPROVAL",
                  "PAUSE_NOT_ALLOWED",
                  "ALREADY_PAUSED",
                  "PAUSE_PAST_CUTOFF",
                  "PAUSE_LIMIT_REACHED",
                  "FORBIDDEN",
                  "PLAN_REQUIRED"
                ],
                "description": "Why canPause is false; null when it is true."
              },
              "canPauseReason": {
                "type": "string",
                "nullable": true,
                "example": "You have used all 5 pauses you can have in a year."
              },
              "canResume": {
                "type": "boolean"
              },
              "canResumeReason": {
                "type": "string",
                "nullable": true
              },
              "isPaused": {
                "type": "boolean"
              },
              "pausedUntil": {
                "type": "string",
                "format": "date",
                "nullable": true,
                "description": "The last day of the open pause."
              },
              "pausedUntilLabel": {
                "type": "string",
                "nullable": true,
                "example": "12 Oct 2026"
              },
              "pausesRemaining": {
                "type": "integer",
                "nullable": true,
                "description": "Pauses the organisation has left in the last 12 months; null when the return is not waiting for approval."
              },
              "pausesPerYear": {
                "type": "integer",
                "nullable": true
              },
              "allowedEnd": {
                "type": "object",
                "nullable": true,
                "description": "Present only while canPause: the dates the last day of a pause may be chosen from.",
                "properties": {
                  "earliest": {
                    "type": "string",
                    "format": "date"
                  },
                  "latest": {
                    "type": "string",
                    "format": "date"
                  },
                  "earliestLabel": {
                    "type": "string",
                    "example": "6 Oct 2026"
                  },
                  "latestLabel": {
                    "type": "string",
                    "example": "15 Oct 2026"
                  },
                  "maxDays": {
                    "type": "integer",
                    "example": 10
                  }
                }
              },
              "confirmation": {
                "type": "object",
                "nullable": true,
                "description": "Present only while canPause: the wording to show before pausing.",
                "properties": {
                  "title": {
                    "type": "string"
                  },
                  "body": {
                    "type": "string"
                  },
                  "confirmLabel": {
                    "type": "string"
                  }
                }
              }
            }
          },
          "jurisdictionId": {
            "type": "string",
            "example": "US_CA",
            "description": "The jurisdiction of the return: a country code such as `GB` or `DE`, a state such as `US_CA`, or an EU scheme: `EU` (EU sales), `EN` (sales by sellers outside the EU) or `XM` (imports). Use `jurisdictionLabel` to display it."
          },
          "returnType": {
            "type": "string"
          },
          "periodKey": {
            "type": "string",
            "nullable": true,
            "example": "2026-10",
            "description": "A month (`2026-09`), a quarter (`2026-Q3`) or, for any other span, `yyyy-MM-dd_yyyy-MM-dd`. Null when the period is not yet known (live filings only)."
          },
          "periodStart": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "periodEnd": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "dueDate": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "isNil": {
            "type": "boolean",
            "description": "Live filings only: the return is a nil return."
          },
          "paymentDeadline": {
            "type": "string",
            "format": "date",
            "nullable": true,
            "description": "Live filings only."
          },
          "paymentDeadlineLabel": {
            "type": "string",
            "nullable": true,
            "example": "6 Nov 2026",
            "description": "Live filings only."
          },
          "backdated": {
            "type": "boolean",
            "description": "Live filings only, omitted when it could not be determined: true when the return covers a period before the filing start, whatever its status. Show it as a marker on the row."
          },
          "amountLabel": {
            "type": "string",
            "nullable": true,
            "example": "£104.18",
            "description": "Live filings only: the amount formatted with its currency, \"Nil return\" for a nil return, null while the amount is not yet calculated. Render it as is."
          },
          "filingClass": {
            "type": "string",
            "enum": [
              "go_forward",
              "backdated"
            ],
            "description": "Stored filings only. `go_forward` for returns Clearvo prepares from your calculated transactions."
          },
          "status": {
            "type": "string",
            "enum": [
              "DRAFT",
              "PENDING_APPROVAL",
              "PAUSED",
              "APPROVED",
              "SUBMITTED",
              "FILED",
              "NOT_FILED",
              "REQUESTED",
              "IN_PREPARATION",
              "READY_FOR_APPROVAL",
              "NEEDS_ATTENTION"
            ],
            "description": "The filing status. Live filings use only IN_PREPARATION, READY_FOR_APPROVAL, APPROVED, FILED and NEEDS_ATTENTION. See the lifecycle in the Filings docs."
          },
          "statusLabel": {
            "type": "string",
            "example": "Ready for your approval",
            "description": "Display text for status; render it as is."
          },
          "jurisdictionLabel": {
            "type": "string",
            "example": "California",
            "description": "The state or country name, never a code."
          },
          "periodLabel": {
            "type": "string",
            "example": "September 2026"
          },
          "dueDateLabel": {
            "type": "string",
            "nullable": true,
            "example": "20 Nov 2026"
          },
          "nextStepLabel": {
            "type": "string",
            "nullable": true,
            "example": "Approves automatically on 5 Oct",
            "description": "What happens next: the automatic approval cutoff while PENDING_APPROVAL, the pause end while PAUSED. Null when nothing is pending."
          },
          "paymentStatus": {
            "type": "string"
          },
          "currency": {
            "type": "string"
          },
          "amountDue": {
            "type": "number",
            "nullable": true,
            "description": "Null for a live filing whose amount is not yet calculated."
          },
          "taxCollected": {
            "type": "number",
            "description": "Stored filings only."
          },
          "snapshotVersion": {
            "type": "integer",
            "description": "Stored filings only."
          },
          "pauseEndsAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "actionNeeded": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string"
                },
                "label": {
                  "type": "string",
                  "description": "Live filings: what to do and by when, e.g. \"Approve before 5 Oct 2026\"; empty when there is no due date. Never repeats statusLabel."
                }
              }
            }
          },
          "allowedActions": {
            "type": "object",
            "properties": {
              "approve": {
                "type": "boolean"
              },
              "undoApproval": {
                "type": "boolean"
              },
              "pause": {
                "type": "boolean"
              },
              "resume": {
                "type": "boolean"
              },
              "skip": {
                "type": "boolean"
              },
              "editPrepayment": {
                "type": "boolean"
              }
            }
          }
        }
      },
      "FilingDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/FilingSummary"
          },
          {
            "type": "object",
            "properties": {
              "approveConfirmation": {
                "type": "object",
                "description": "Live filings only, present only when `allowedActions.approve` is true: the wording to show before approving. Render it as is.",
                "properties": {
                  "title": {
                    "type": "string",
                    "example": "Approve this return?"
                  },
                  "body": {
                    "type": "string",
                    "example": "Once approved we file it with the tax authority before the due date. An approved return cannot be changed or withdrawn."
                  },
                  "confirmLabel": {
                    "type": "string",
                    "example": "Approve return"
                  }
                }
              },
              "documents": {
                "type": "array",
                "description": "Live filings only: the documents the return has produced (draft, return file, supporting workbook, proof of submission). Download each from `downloadPath`.",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "name": {
                      "type": "string",
                      "example": "VAT return draft.pdf"
                    },
                    "label": {
                      "type": "string",
                      "example": "Draft return",
                      "description": "Display text for the kind of document."
                    },
                    "downloadPath": {
                      "type": "string",
                      "example": "/v1/filings/ret_656200/documents/11111111-2222-4333-8444-555555555555"
                    }
                  }
                }
              },
              "contentMissing": {
                "type": "boolean"
              },
              "totals": {
                "type": "object",
                "additionalProperties": {
                  "type": "number"
                }
              },
              "lines": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "rate": {
                      "type": "number",
                      "nullable": true,
                      "description": "Fractional rate, e.g. 0.0725."
                    },
                    "rateLabel": {
                      "type": "string",
                      "example": "7.25%",
                      "description": "The rate as a percentage for display; \"-\" when the line has no rate."
                    }
                  }
                }
              },
              "totalLine": {
                "type": "object",
                "nullable": true
              },
              "adjustments": {
                "type": "array",
                "items": {
                  "type": "object"
                }
              },
              "scopeNotes": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "What the return includes and excludes, in words."
              },
              "approval": {
                "type": "object",
                "properties": {
                  "by": {
                    "type": "string",
                    "nullable": true,
                    "enum": [
                      "customer",
                      "system"
                    ]
                  },
                  "at": {
                    "type": "string",
                    "format": "date-time",
                    "nullable": true
                  },
                  "channel": {
                    "type": "string",
                    "nullable": true
                  }
                }
              },
              "policy": {
                "type": "object"
              },
              "payment": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string"
                  },
                  "method": {
                    "type": "string",
                    "nullable": true
                  },
                  "initiatedOn": {
                    "type": "string",
                    "format": "date",
                    "nullable": true
                  },
                  "nothingToPay": {
                    "type": "boolean"
                  },
                  "note": {
                    "type": "string",
                    "nullable": true
                  }
                }
              },
              "bank": {
                "type": "object",
                "properties": {
                  "required": {
                    "type": "boolean"
                  },
                  "account": {
                    "type": "object",
                    "nullable": true,
                    "properties": {
                      "last4": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "filed": {
                "type": "object",
                "properties": {
                  "confirmationNumber": {
                    "type": "string",
                    "nullable": true
                  },
                  "filedAmount": {
                    "type": "number",
                    "nullable": true
                  },
                  "differenceReason": {
                    "type": "string",
                    "nullable": true
                  },
                  "proofAvailable": {
                    "type": "boolean"
                  }
                }
              },
              "autoApproveBlockers": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "timeline": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "type": {
                      "type": "string"
                    },
                    "label": {
                      "type": "string"
                    },
                    "at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "actor": {
                      "type": "string",
                      "enum": [
                        "customer",
                        "system",
                        "operations"
                      ]
                    }
                  }
                }
              }
            }
          }
        ]
      },
      "FilingUploadDeclaration": {
        "type": "object",
        "description": "The declaration the person confirming accepts. Show `text`, then send back `version`.",
        "properties": {
          "version": {
            "type": "string",
            "example": "2026-10-13.1"
          },
          "text": {
            "type": "string",
            "example": "I confirm that I have added all my data for this month. I am responsible for the completeness and accuracy of the data I add. If any row in a file has an error, the whole file is not used until you fix it and upload it again. We do not verify your data."
          }
        }
      },
      "FilingUploadWindow": {
        "type": "object",
        "description": "When the month takes data. `opensAt` is when the period has closed. `closesAt` is the end of the day on which the return is approved automatically, the 5th of the following month by default.",
        "properties": {
          "state": {
            "type": "string",
            "enum": [
              "not_yet_open",
              "open",
              "closed",
              "locked"
            ]
          },
          "opensAt": {
            "type": "string",
            "format": "date-time"
          },
          "closesAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "lockReason": {
            "type": "string",
            "nullable": true,
            "enum": [
              "filing_approved",
              "filing_submitted",
              "return_submitted"
            ],
            "description": "Why the month is locked: `filing_approved` (undo the approval to change data) or the return has been submitted (final)."
          }
        }
      },
      "FilingUploadFile": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "sizeBytes": {
            "type": "integer"
          },
          "source": {
            "type": "string",
            "enum": [
              "customer",
              "system"
            ],
            "description": "`system` is the file of transactions Clearvo calculated for you; it is included automatically and cannot be deleted."
          },
          "system": {
            "type": "boolean"
          },
          "deletable": {
            "type": "boolean"
          },
          "note": {
            "type": "string"
          },
          "uploadedAt": {
            "type": "string",
            "format": "date-time"
          },
          "status": {
            "type": "string",
            "enum": [
              "uploaded",
              "processed",
              "had_errors",
              "failed"
            ],
            "description": "`uploaded` = not processed yet. `processed` = read without errors. `had_errors` = a row has an error, so the whole file is not used (download the error and warning report). `failed` = the file could not be processed."
          },
          "statusLabel": {
            "type": "string",
            "description": "Display text for `status`; render it as is."
          },
          "transactionCount": {
            "type": "integer",
            "nullable": true,
            "description": "Null until the file has been processed."
          },
          "errorCount": {
            "type": "integer",
            "nullable": true,
            "description": "Rows with an error. Null until the file has been processed."
          },
          "warningCount": {
            "type": "integer",
            "nullable": true
          },
          "errorReportAvailable": {
            "type": "boolean"
          },
          "errorReportLabel": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "FilingUploadMonthSummary": {
        "type": "object",
        "properties": {
          "period": {
            "type": "string",
            "example": "2026-09"
          },
          "periodLabel": {
            "type": "string",
            "example": "September 2026"
          },
          "kind": {
            "type": "string",
            "description": "`go_forward` for months of your current filing. Months managed in the dashboard may carry another value."
          },
          "state": {
            "type": "string",
            "enum": [
              "not_open",
              "open",
              "processing",
              "done",
              "errors",
              "closed",
              "locked",
              "unavailable"
            ],
            "description": "not_open = the period has not finished yet. open = takes data. processing = your data is being processed. done = processed without errors. errors = a file has errors (the month cannot be confirmed until the file is fixed or removed) or a file failed. closed = read-only, kept for older months (the month's `message` says to contact support). locked = the return is approved or submitted. unavailable = the month could not be checked just now."
          },
          "stateLabel": {
            "type": "string"
          },
          "windowLabel": {
            "type": "string",
            "nullable": true,
            "example": "Open until 5 Oct 2026"
          },
          "window": {
            "$ref": "#/components/schemas/FilingUploadWindow"
          }
        }
      },
      "FilingUploadMonth": {
        "allOf": [
          {
            "$ref": "#/components/schemas/FilingUploadMonthSummary"
          },
          {
            "type": "object",
            "properties": {
              "message": {
                "type": "string",
                "nullable": true,
                "description": "A sentence for the closed, locked, not yet open, processing and unavailable states, and while any file has errors; render it as is."
              },
              "contactUrl": {
                "type": "string",
                "description": "Where to contact support: for a closed month, or a file prepared for you that has errors."
              },
              "confirmation": {
                "type": "object",
                "nullable": true,
                "description": "Set once the month has been confirmed.",
                "properties": {
                  "confirmedAt": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "confirmedBy": {
                    "type": "string"
                  },
                  "declarationVersion": {
                    "type": "string"
                  }
                }
              },
              "declaration": {
                "$ref": "#/components/schemas/FilingUploadDeclaration"
              },
              "counts": {
                "type": "object",
                "description": "Totals over the processed files.",
                "properties": {
                  "transactions": {
                    "type": "integer"
                  },
                  "errors": {
                    "type": "integer"
                  },
                  "warnings": {
                    "type": "integer"
                  }
                }
              },
              "canUpload": {
                "type": "boolean"
              },
              "canConfirm": {
                "type": "boolean",
                "description": "True when the month is open, has at least one file and has not been processed since the last change. False while any file has errors (`message` says what to do, with `contactUrl` for a file prepared for you)."
              },
              "allowedActions": {
                "type": "object",
                "description": "`process` is false while any file has errors.",
                "properties": {
                  "upload": {
                    "type": "boolean"
                  },
                  "delete": {
                    "type": "boolean"
                  },
                  "process": {
                    "type": "boolean"
                  }
                }
              },
              "files": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/FilingUploadFile"
                }
              }
            }
          }
        ]
      },
      "FilingUploadMonthList": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "linked": {
            "type": "boolean",
            "description": "False until the business profile is set up for filing; `months` is then empty and `message` says so."
          },
          "months": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FilingUploadMonthSummary"
            }
          },
          "openPeriod": {
            "type": "string",
            "nullable": true,
            "description": "The newest month that is open for data."
          },
          "message": {
            "type": "string"
          }
        }
      },
      "FilingUploadMonthResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "alreadyApplied": {
            "type": "boolean"
          },
          "month": {
            "$ref": "#/components/schemas/FilingUploadMonth"
          }
        }
      },
      "FilingUploadFileResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "file": {
            "$ref": "#/components/schemas/FilingUploadFile"
          }
        }
      },
      "FilingUploadDeleted": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "deleted": {
            "type": "boolean"
          },
          "id": {
            "type": "string"
          }
        }
      },
      "FilingUploadErrorReport": {
        "type": "object",
        "description": "A fresh download link for the error and warning report of a file. `url` is new on every call and is for the caller only.",
        "required": [
          "ok",
          "name",
          "url",
          "label"
        ],
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "name": {
            "type": "string",
            "description": "File name of the report."
          },
          "url": {
            "type": "string",
            "description": "Download link (https only). Do not store it."
          },
          "label": {
            "type": "string",
            "description": "Text to show on the download link, as is."
          }
        }
      },
      "FilingUploadDeclarationError": {
        "type": "object",
        "description": "The declaration was not accepted (`DECLARATION_REQUIRED`) or its version is out of date (`DECLARATION_VERSION_MISMATCH`).",
        "properties": {
          "error": {
            "type": "string",
            "enum": [
              "DECLARATION_REQUIRED",
              "DECLARATION_VERSION_MISMATCH"
            ]
          },
          "message": {
            "type": "string"
          },
          "currentVersion": {
            "type": "string",
            "description": "DECLARATION_VERSION_MISMATCH only."
          },
          "declaration": {
            "$ref": "#/components/schemas/FilingUploadDeclaration"
          }
        }
      },
      "FilingUploadTemplate": {
        "type": "object",
        "properties": {
          "version": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "dateFormat": {
            "type": "string"
          },
          "decimalSeparator": {
            "type": "string"
          },
          "columns": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "column": {
                  "type": "string"
                },
                "required": {
                  "type": "boolean"
                },
                "format": {
                  "type": "string"
                },
                "example": {
                  "type": "string"
                },
                "guide": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "FilingError": {
        "type": "object",
        "description": "The error body of the Filings routes: `error` is the stable code, `message` or `detail` the sentence.",
        "properties": {
          "error": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "detail": {
            "type": "string"
          }
        }
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "x-idempotency-key",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string",
          "pattern": "^[A-Za-z0-9_-]{1,128}$",
          "minLength": 1,
          "maxLength": 128
        },
        "description": "Optional on POST /send. 1-128 characters: letters, digits, hyphen or underscore (a UUID is fine); anything else is refused 400 INVALID_IDEMPOTENCY_KEY. This is only YOUR retry hint; it is stored separately and never decides whether two documents are the same. Every document gets a system-generated identity key, `sys-<40 hex>`, built from its own fields (a sale: documentType + country + invoiceNumber; a purchase: also supplier.taxId and issueDate; a NATIVE_XML document: the hash of the document), and that key alone deduplicates. It is echoed, with its source (`system` or `document`), in the `X-Idempotency-Key` and `X-Idempotency-Key-Source` response headers. Re-sending the same identity with different content is refused 409 IDEMPOTENT_BODY_MISMATCH instead of silently replaying the first result (an earlier NEEDS_INFO, REJECTED or held-on-tax-code attempt is re-run in place), whatever `x-idempotency-key` you send. Reusing your key for a DIFFERENT document is not refused: the document is processed under its own identity and the response carries the warning IDEMPOTENCY_KEY_REUSED_ACROSS_DOCUMENTS in `warnings[]`. A document missing its identity fields is not refused either: your key, when you send one, becomes its identity, otherwise Clearvo mints one and the response carries the warning NO_IDENTITY_NO_DEDUP (a re-send then cannot be matched to it)."
      },
      "IdempotencyKeyRequired": {
        "name": "x-idempotency-key",
        "in": "header",
        "required": true,
        "schema": {
          "type": "string",
          "pattern": "^[A-Za-z0-9_-]{1,128}$",
          "minLength": 1,
          "maxLength": 128
        },
        "description": "Required on this endpoint (400 otherwise): 1-128 characters, letters, digits, hyphen or underscore. Replaying the same key returns the stored result without re-submitting to the authority."
      },
      "EntityId": {
        "name": "x-entity-id",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string",
          "format": "uuid"
        },
        "description": "Required when using an organisation-scoped API key (omit entirely for an entity-scoped key, which resolves its one entity automatically and rejects a mismatching value here with 400). Must be one of the organisation's entities; if the organisation-scoped key is restricted to a subset of entities, must be one of that key's allowed entities or the request is rejected with 403."
      },
      "FilingId": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "example": "fil_0b6e1f6c0a7d4c6e9f2b1d3a5c7e9f00"
        }
      },
      "FilingUploadMonth": {
        "name": "month",
        "in": "path",
        "required": true,
        "description": "The month as yyyy-MM.",
        "schema": {
          "type": "string",
          "pattern": "^\\d{4}-(0[1-9]|1[0-2])$",
          "example": "2026-09"
        }
      },
      "FilingUploadFileId": {
        "name": "fileId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "The file id from `GET /filings/upload/months/{month}`."
      }
    }
  },
  "paths": {
    "/invoices": {
      "get": {
        "operationId": "listInvoices",
        "summary": "List invoices",
        "description": "Paginated list of submitted and received invoices, newest first. Supports filtering by country, status, direction, document type, entity, submission and clearance date ranges, free-text search, business status and Brazil document family. Offset pagination (page) by default; cursor pagination (after_id/before_id) is preferred for large datasets.",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "country",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Filter by ISO country code (exact match)."
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/ClearanceStatus"
                },
                {
                  "type": "string",
                  "enum": [
                    "failed"
                  ]
                }
              ]
            },
            "description": "Filter by clearance status (case-insensitive). The special alias `failed` matches REJECTED/ERROR/failed rows and any row carrying an errorCode, and additionally returns errorCode, isMerchantResolvable and suggestedAction on each row."
          },
          {
            "name": "direction",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "outbound",
                "inbound"
              ]
            },
            "description": "Filter to invoices you submitted (\"outbound\") or invoices you received from a supplier (\"inbound\" — Peppol, Poland KSeF via POST /pl/inbound/poll, France via POST /fr/inbound/poll, or Mexico CFDI via POST /mx/inbound/poll or POST /mx/inbound/cfdi). Omit to return both directions interleaved."
          },
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Only invoices submitted on or after this date (YYYY-MM-DD, matched against submittedAt)."
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Only invoices submitted on or before this date (YYYY-MM-DD, matched against submittedAt)."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 50,
              "maximum": 200
            },
            "description": "Rows per page."
          },
          {
            "name": "after_id",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Cursor (keyset) pagination: return invoices submitted before this invoice ID. Use pagination.nextCursor from a previous response. When a cursor parameter is supplied, `page` is ignored. 400 if the ID is not found in your scope."
          },
          {
            "name": "before_id",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Cursor (keyset) pagination: return invoices submitted after this invoice ID. Use pagination.prevCursor from a previous response."
          },
          {
            "name": "receivedAfter",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "A monotonic ingestion cursor: only invoices whose Clearvo-side created_at is strictly after this ISO 8601 timestamp. Deliberately distinct from `from`/`to` (day-granularity submittedAt) — use this to poll \"what's new since I last checked\", most relevant for inbound Mexico CFDIs where SAT's own backend can publish a document days after its issue date. 400 if not a valid timestamp."
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            },
            "description": "1-based page number (offset pagination, the default). Ignored when after_id or before_id is supplied."
          },
          {
            "name": "documentType",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "invoice",
                "credit_note",
                "debit_note"
              ]
            },
            "description": "Filter by document type."
          },
          {
            "name": "entityId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organisation-scoped keys only: restrict the list to one entity (403 if the key's entity allowlist excludes it). Ignored for entity-scoped keys."
          },
          {
            "name": "clearedFrom",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Only invoices cleared on or after this date (YYYY-MM-DD, matched against clearedAt)."
          },
          {
            "name": "clearedTo",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Only invoices cleared on or before this date (YYYY-MM-DD, matched against clearedAt)."
          },
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 200
            },
            "description": "Case-insensitive partial match on invoice number or customerReference."
          },
          {
            "name": "erpOrganizationId",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Only records synced from this ERP connection (e.g. a Zoho Books organization_id)."
          },
          {
            "name": "excludedList",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "taxId",
                "customerRef",
                "name"
              ]
            },
            "description": "Narrows EXCLUDED rows to those excluded by a customer-exclusion rule matching on this list."
          },
          {
            "name": "businessStatus",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Filter by business status: either a shared canonical value (RECEIVED, ACKNOWLEDGED, ACCEPTED, DISPUTED, matching every native status that maps onto it) or a native status from a registered regime (e.g. Brazil's CIENCIA). 400 for an unrecognised value."
          },
          {
            "name": "documentFamily",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "nfe",
                "nfse"
              ]
            },
            "description": "Brazil only: goods (nfe) vs services (nfse) national document family. Matches nothing for other countries. 400 for any other value."
          }
        ],
        "responses": {
          "403": {
            "description": "Either this API key is not authorized for the specified entity (entity-scoped key against a different entity, or an organisation-scoped key's allowlist does not include the entity named in x-entity-id), or the request is purchase-side (transactionDirection `purchase` or documentStage `purchase_order`) and Purchases (AP) is not available: `ap_not_included` (Enterprise plan required) or `ap_not_enabled` (switch it on under Settings > Solutions).",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    { "$ref": "#/components/schemas/Error" },
                    { "$ref": "#/components/schemas/ApNotAvailable" }
                  ]
                }
              }
            }
          },
          "200": {
            "description": "List of invoices",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "invoices",
                    "pagination"
                  ],
                  "properties": {
                    "invoices": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/InvoiceListItem"
                      }
                    },
                    "pagination": {
                      "type": "object",
                      "description": "Cursor mode (after_id/before_id supplied) returns limit, hasNext, hasPrev, nextCursor, prevCursor. Offset mode additionally returns total, page and pages.",
                      "required": [
                        "limit",
                        "hasNext",
                        "hasPrev"
                      ],
                      "properties": {
                        "total": {
                          "type": "integer",
                          "description": "Offset mode only."
                        },
                        "page": {
                          "type": "integer",
                          "description": "Offset mode only."
                        },
                        "pages": {
                          "type": "integer",
                          "description": "Offset mode only."
                        },
                        "limit": {
                          "type": "integer"
                        },
                        "hasNext": {
                          "type": "boolean"
                        },
                        "hasPrev": {
                          "type": "boolean"
                        },
                        "nextCursor": {
                          "type": "string",
                          "nullable": true,
                          "description": "Pass as after_id to retrieve the next page. Null when the page is not full."
                        },
                        "prevCursor": {
                          "type": "string",
                          "nullable": true,
                          "description": "Pass as before_id to retrieve the previous page. Null for an empty page."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "400": {
            "description": "Invalid filter — a date not in YYYY-MM-DD form, direction not inbound/outbound, unknown documentFamily or businessStatus, invalid receivedAfter, or a cursor ID that does not exist in your scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Failed to query invoices",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/send": {
      "post": {
        "operationId": "submitInvoice",
        "summary": "Submit an invoice",
        "description": "Submit a transaction for processing — the single entry point for every compliance mechanism. The platform validates the document, resolves its Compliance Mandate (which regulatory obligation applies), and dispatches accordingly, invisibly to the caller: a per-document e-invoicing mandate generates the country's XML format, submits to the tax authority or Peppol network, and returns the clearance status (200); an aggregate e-reporting mandate (e.g. France Flux 2) accepts the transaction into a reporting period for later aggregate submission (200 ACCUMULATED — never submit e-reporting data anywhere else); no applicable obligation is recorded and retained (200, outcome NO_OBLIGATION). Every successful response carries the shared envelope fields `mandate`/`outcome`/`disposition`/`state` — branch on `outcome`. Before dispatch, the shared validation-rules registry runs: content-team-managed ADJUSTMENT rules auto-correct eligible fields first (see `adjustments` on the response, and `rejectInsteadOfAutoCorrect` to opt out), then every applicable ERROR/WARNING check is collected (never fail-fast) — see GET /v1/validations for the live catalogue of every check this endpoint can emit. Customer name and city are only required when the transaction actually resolves to a document-generating (e-invoicing) mandate — an aggregate e-reporting row does not need them; customer country, dates, amounts, and currency are always required. Same-key retries of a non-terminal outcome (SETUP_NEEDED, NEEDS_INFO, OBLIGATION_DISABLED) are re-evaluated against current setup rather than replaying the stored response; terminal outcomes replay. The response carries `reevaluated: true` only when a previously held transaction was released or re-judged on retry (for example a resolved HELD_UNMAPPED_DECISION hold resolving to NO_MANDATE, or a France e-reporting retry); other re-runs return the normal body for the new outcome. For a stored customer (`customer.customerRef`), the customer's human-confirmed PEPPOL_PARTICIPANT_ID reference and its LEITWEG_ID reference (see GET /v1/customer-reference-types) fill in the Peppol endpoint and `countrySpecific.de.leitwegId` only when the request supplies neither the explicit value (the `electronicAddress` pair; `countrySpecific.de.leitwegId` or a non-empty `buyerReference`); an explicit value always wins. A German XRechnung or Peppol document needs an electronic address for the customer and for the supplier, or it is blocked before any XML is generated with ERROR `MISSING_CUSTOMER_ELECTRONIC_ADDRESS` or `MISSING_SUPPLIER_ELECTRONIC_ADDRESS`; the order they are resolved in is described on `countrySpecific`. LIFECYCLE: a well-formed document is always accepted, stored in full before it is processed, and answered 200 (a rejection, a hold, an accumulated report and a document still being processed all included). The processing result is in the body, so a stored rejection or hold is a status, not a lost request: a 200 means stored, not accepted. Read `outcome`, `clearanceStatus`, `statusClass`, `nextStep` and `terminal`, never the HTTP code alone. Only a request that cannot be read or is not allowed is a 4xx: unparseable JSON, wrong types or an incomplete envelope (400), authentication or plan (401/403), size (413), rate limit (429), and a changed re-send of an already processed document (409 IDEMPOTENT_BODY_MISMATCH; the refused edit is recorded as a `refused_resend` event on the stored record and the stored document is not changed). Every exit that stores the document, including those that answer with `errors[]`, carries `id`, the stored record id. IDENTITY: the document's own fields identify it (documentType + country + invoiceNumber, and for a purchase the supplier tax ID and issue date). A document without those fields is no longer refused: it is stored under an identity Clearvo mints and `warnings[]` carries NO_IDENTITY_NO_DEDUP (duplicates of it cannot be detected). An `x-idempotency-key` reused for a different document is no longer refused with 409: the document is processed under its own identity and `warnings[]` carries IDEMPOTENCY_KEY_REUSED_ACROSS_DOCUMENTS. SUPPORT: a send for a country that is not yet enabled for your account is answered 200 with clearanceStatus HELD_AWAITING_AUTHORIZATION and errorCode MANDATE_SUPPORT_NOT_OPERATIONAL. Nothing is needed from you: it is released automatically when support opens. A document dated before the date Clearvo supports that mandate is NOT_SUPPORTED (terminal, 200, errorCode MANDATE_NOT_SUPPORTED) and is never submitted later. Inside the request: envelope check, store, normalise, enrich, validation rules, tax code and mandate resolution, setup checks, document generation and schema check. Countries that issue the document themselves (Spain VeriFactu, Portugal, Germany) finish inside the request (terminal: true). Countries where an authority or network answers (Italy, Poland, Romania, Hungary, Argentina, France, Saudi Arabia, Peppol) return PENDING with `nextPollAfter` and finish afterwards: poll GET /v1/status or subscribe to the `invoice.status_changed` webhook. A document that is waiting for you (`statusClass` WAIT_MERCHANT) never expires; `nextStep` says what to fix and where. NO_MANDATE and NOT_SUPPORTED are finished outcomes, not errors. A replay of a document that is still being processed returns its `id` and `clearanceStatus` RECEIVED. See https://clearvo.io/docs/e-invoice-lifecycle.",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InvoiceRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The document is stored. HTTP 200 covers every stored outcome: cleared or pending under a per-document mandate, accumulated into a report (`ACCUMULATED`), received and still processing (`RECEIVED`), no obligation, a hold (`HELD_*`), not supported yet (`NOT_SUPPORTED`), and a rejection (`REJECTED`) or a document that needs information (`NEEDS_INFO`, with `error` and `errors[]`). The outcome is in the body, never in the status code, and 200 means stored, not accepted: read `outcome`, `clearanceStatus`, `statusClass` and `nextStep`, and poll if `terminal` is false. `ok` is not the outcome: it is derived from the status alone, `false` for `REJECTED`, `NEEDS_INFO`, `SETUP_NEEDED` and `HELD_PLATFORM_FAULT` and `true` otherwise, and the same on every replay of the document (a replay carries the same `error`, `errors[]`, `fixUrl` and `nextStep`), so branch on `outcome`. `outcome` is one of `CLEARED`, `ACCUMULATED`, `NEEDS_INFO`, `HELD`, `REJECTED`, `NO_OBLIGATION`, `PROCESSING` (stored, not processed yet: poll GET /v1/status or wait for the `invoice.status_changed` webhook) An unroutable Peppol customer (`UNROUTABLE`) is `NEEDS_INFO`. A rejection carries `error` (`code`, `message`, `fixUrl`, `isMerchantResolvable`, `retryable`) and one `errors[]` entry per failed check, each with a JSON Pointer `path` and a stable `errorCode`. The codes a validation check, a country generator or a document format check can return (`FATTURAPA_SCHEMA_VALIDATION_FAILED`, `PEPPOL_CUSTOMER_UNREACHABLE`, `MISSING_SELLER_PEPPOL_ID`, `CREDIT_NOTE_PEPPOL_UNSUPPORTED` and the rest of the catalogue in GET /v1/validations) are `error.code` values in this body. `id` is always the stored record's id, including on a rejection or hold. If a temporary problem interrupts processing after the document is stored, the response is 200 `RECEIVED` with `id` and the warning `PROCESSING_DELAYED`: do not resend, Clearvo finishes the document and you can poll GET /v1/status or subscribe to `invoice.status_changed`. A dry run (`dryRun: true`) answers 200 with the same body, `dryRun: true` and `ok: false`, and the `clearanceStatus` and `errors[]` a real send would store; nothing is stored. `warnings[]` can carry IDEMPOTENCY_KEY_REUSED_ACROSS_DOCUMENTS (your `x-idempotency-key` was already used for a different document; this one was processed under its own identity), NO_IDENTITY_NO_DEDUP (the document has no identifying fields, so it was stored under a minted key and a re-send cannot be matched to it) and PROCESSING_DELAYED. A send for a country that is not yet enabled for your account returns clearanceStatus HELD_AWAITING_AUTHORIZATION with `errorCode: MANDATE_SUPPORT_NOT_OPERATIONAL`: nothing is needed from you and it is released automatically. A document dated before the date Clearvo supports the mandate returns NOT_SUPPORTED with `errorCode: MANDATE_NOT_SUPPORTED`, terminal, and is never submitted later. `adjustments`/`warnings`/`rulesetVersion` are always present, even on success. A NEEDS_INFO outcome (aggregate e-reporting mandate, missing required field) additionally carries `errorCode: MANDATE_INCOMPLETE_DATA`, `missingFields[]`, the affected `period`, and its filing `deadline`. A NEEDS_INFO outcome with `errorCode: MISSING_SUPPLIER_REGISTRATION` means the invoice resolved to Spain SII/VeriFactu but the entity has no current Spanish registration as of the document date (its `errorCode: MISSING_CUSTOMER_REGISTRATION` counterpart is the same condition on a `transactionDirection: 'purchase'` received-book document, where the entity is the `customer`) — nothing is filed until one is added (`fixUrl`); resubmit under a new x-idempotency-key afterwards. A HELD outcome with `errorCode: HELD_UNMAPPED_DECISION` can occur for ANY country (not only Spain): the tax-mandate engine found no compliance-mandate rule for this transaction's tax decision — a platform configuration gap, `actionOwner: platform`, released automatically once a rule is added. Resend a corrected document under the same invoice number to replace a stored rejection; an identical resend is judged again against the current records and answers 200 with the stored outcome (`id`, `outcome`, `errorCode`).",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/InvoiceResponse"
                    },
                    {
                      "$ref": "#/components/schemas/InvoiceAccumulatedResponse"
                    },
                    {
                      "$ref": "#/components/schemas/InvoiceRejectedResponse"
                    },
                    {
                      "$ref": "#/components/schemas/PintAunzRejectedResponse"
                    },
                    {
                      "$ref": "#/components/schemas/PeppolCustomerUnreachableResponse"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request is malformed: invalid JSON, a missing `country` or `invoiceNumber`, a malformed x-idempotency-key (INVALID_IDEMPOTENCY_KEY; the header is optional), an unrecognised country code, or a platform-set field supplied by the caller (`channel`, `source`: CHANNEL_FIELD_NOT_ACCEPTED). A well-formed document is never answered with an error status for what is wrong in it: that is a stored rejection, answered 200.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-errorCodes": [
              "INVALID_IDEMPOTENCY_KEY",
              "CHANNEL_FIELD_NOT_ACCEPTED"
            ]
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "The entity could not be found for this key (code ENTITY_NOT_FOUND) — typically a sandbox request against an entity that has not been mirrored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-errorCodes": [
              "ENTITY_NOT_FOUND"
            ]
          },
          "409": {
            "description": "Two distinct cases. (1) A conflicting stored record, for example DUPLICATE_INVOICE_NUMBER (the invoice number is already used by this entity) or ALREADY_CORRECTED: the body says what to change. (2) IDEMPOTENT_BODY_MISMATCH: an invoice with the same identity (documentType + country + invoiceNumber, and for a purchase the supplier tax ID and issue date) already exists with DIFFERENT content and a status that cannot be re-run (anything other than NEEDS_INFO, REJECTED or held-on-tax-code, which are re-run in place). The document has already been processed and is not changed; the refused edit is recorded as a `refused_resend` event on the stored record. Resend the original body to replay the stored result, or send a correction (`correctsInvoiceId`) or a credit note. A re-sent identical document is answered 200 with the stored result, not 409. IDEMPOTENCY_KEY_REUSED no longer exists: a key reused for a different document is processed under its own identity and returns the warning IDEMPOTENCY_KEY_REUSED_ACROSS_DOCUMENTS in `warnings[]`.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/InvoiceResponse"
                    },
                    {
                      "type": "object",
                      "required": [
                        "ok",
                        "error"
                      ],
                      "properties": {
                        "ok": {
                          "type": "boolean",
                          "enum": [
                            false
                          ]
                        },
                        "error": {
                          "type": "object",
                          "required": [
                            "code",
                            "message"
                          ],
                          "properties": {
                            "code": {
                              "type": "string",
                              "enum": [
                                "IDEMPOTENT_BODY_MISMATCH"
                              ]
                            },
                            "message": {
                              "type": "string"
                            },
                            "retryable": {
                              "type": "boolean",
                              "enum": [
                                false
                              ]
                            }
                          }
                        },
                        "id": {
                          "type": "string",
                          "description": "The existing invoice record that holds this identity."
                        },
                        "clearanceStatus": {
                          "type": "string"
                        },
                        "hint": {
                          "type": "string"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "413": {
            "description": "The request body is too large.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "description": "Unexpected server error. Safe to retry with the same x-idempotency-key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Nothing could be stored. `INGEST_UNAVAILABLE`: the document could not be saved and the fallback queue is unavailable too; retry with the same body and the same x-idempotency-key (a Retry-After header is set). Other codes: STORE_FAILED (a rejection could not be saved), VALIDATION_RULES_UNAVAILABLE (the validation rules could not be loaded and the document was not saved: retry), PERIOD_LINK_FAILED (the document is stored as ACCUMULATED but could not be linked to its reporting period: resend with the same x-idempotency-key and it is linked), and, on a dry run only, VALIDATION_UNAVAILABLE (Germany over Peppol or XRechnung: the pre-send validation could not run). A temporary problem after the document is stored is not a 503: it is 200 `RECEIVED`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-errorCodes": [
              "INGEST_UNAVAILABLE",
              "STORE_FAILED",
              "VALIDATION_RULES_UNAVAILABLE",
              "PERIOD_LINK_FAILED",
              "VALIDATION_UNAVAILABLE"
            ]
          }
        }
      }
    },
    "/send/bulk": {
      "post": {
        "operationId": "submitBulkCsv",
        "summary": "Bulk CSV submission",
        "description": "Bulk CSV ingestion through the same unified pipeline as POST /send — every row runs the exact same validation → mandate-resolution → dispatch chain a single-document submit does, so one file can freely mix rows that resolve to e-invoicing, aggregate e-reporting, and no obligation; the platform decides per row, never the caller. Columns (header names lower-case): transaction_date (required, YYYY-MM-DD — the invoice date), tax_point_date (optional — drives mandate effective-date matching and period membership when the VAT point differs from the invoice date), due_date (optional, YYYY-MM-DD — the payment due date), payment_terms (optional free text up to 1000 characters, e.g. \"Paid on delivery\"; a Romanian invoice with an amount to pay needs a due_date or payment_terms, otherwise the row fails with MISSING_PAYMENT_DUE_DATE_OR_TERMS), currency (required, ISO 4217), country (required, ISO 3166-1 alpha-2), net_amount (required), tax_amount (required, >= 0), document_type (invoice|credit_note|debit_note, default invoice), invoice_number, source_reference (drives cross-upload idempotency/dedup), customer_country, customer_vat_number, customer_name (only required if the row resolves to e-invoicing), customer_address_line1, customer_city, customer_postal_code, description, tax_code (your own client tax code as mapped via POST /v1/tax/client-codes; a blank cell receives the platform default S, which must itself be registered for this entity — otherwise the row is held HELD_UNMAPPED_TAX_CODE with reason.codeSource `default`), oss_declared (true|false — explicit OSS declaration override; omitted = platform-derived). Re-uploading a file is safe: rows already processed return SKIPPED_DUPLICATE via the same idempotency machinery as single submits. Transport is chosen by upload size, not by the caller: an upload under 25MB is processed synchronously in the request (the 200 below); an upload of 25MB or more, up to 200MB, is stored and processed asynchronously and answers 202 with a batchId to poll via GET /send/bulk/{batchId}. Rate limits: 30 requests per minute per API key, plus a bulk rows budget of 20,000 CSV data rows per minute per key on the synchronous transport — an upload larger than the budget remaining in the current window is rejected whole with 429 and nothing in it is processed, so split the file or wait for the window to reset. Asynchronous uploads are not row-budgeted. The X-RateLimit-Rows-* response headers show the remaining budget; see the Rate limits section of the API overview.",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "The CSV file."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Per-row results. `summary` counts every outcome; `rows[]` carries one entry per CSV row.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-RateLimit-Scope": {
                "$ref": "#/components/headers/X-RateLimit-Scope"
              },
              "X-RateLimit-Rows-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Rows-Limit"
              },
              "X-RateLimit-Rows-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Rows-Remaining"
              },
              "X-RateLimit-Rows-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Rows-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "summary": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "integer"
                        },
                        "accepted": {
                          "type": "integer",
                          "description": "Rows that cleared (or are pending clearance) under a per-document mandate."
                        },
                        "accumulated": {
                          "type": "integer",
                          "description": "Rows folded into an aggregate reporting period."
                        },
                        "needsInfo": {
                          "type": "integer"
                        },
                        "skippedDuplicate": {
                          "type": "integer"
                        },
                        "held": {
                          "type": "integer"
                        },
                        "noObligation": {
                          "type": "integer"
                        },
                        "errored": {
                          "type": "integer"
                        }
                      }
                    },
                    "rows": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "rowNumber": {
                            "type": "integer"
                          },
                          "outcome": {
                            "type": "string",
                            "enum": [
                              "ACCEPTED",
                              "ACCUMULATED",
                              "NEEDS_INFO",
                              "SKIPPED_DUPLICATE",
                              "HELD",
                              "NO_OBLIGATION",
                              "ERRORED"
                            ]
                          },
                          "id": {
                            "type": "string",
                            "nullable": true,
                            "description": "The transaction's Clearvo id when it reached dispatch."
                          },
                          "errorCode": {
                            "type": "string",
                            "nullable": true,
                            "description": "Set when outcome is ERRORED (or a per-row validation failure), e.g. MISSING_OR_INVALID_FIELD naming the offending column."
                          },
                          "errorMessage": {
                            "type": "string",
                            "nullable": true
                          },
                          "recordId": {
                            "type": "string",
                            "nullable": true,
                            "description": "The stored record's id, also on a row that was stored as a rejection (`outcome` ERRORED with a `clearanceStatus` of REJECTED or NEEDS_INFO)."
                          },
                          "clearanceStatus": {
                            "type": "string",
                            "nullable": true,
                            "description": "The status the row's document was stored with, when it was stored."
                          }
                        }
                      }
                    },
                    "truncated": {
                      "type": "boolean",
                      "description": "True when the file exceeded the row cap and remaining rows were not processed."
                    }
                  }
                }
              }
            }
          },
          "202": {
            "description": "Upload of 25MB or more (up to 200MB) — accepted and queued for asynchronous processing rather than returning per-row results in this response. Poll GET /send/bulk/{batchId} for progress and final per-outcome counts.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "batchId": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "UPLOADED"
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Not multipart/form-data, no file field, or not a .csv file",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/mandate-transactions": {
      "get": {
        "operationId": "listMandateTransactions",
        "summary": "List mandate transactions",
        "description": "The consolidated cross-jurisdiction status surface over the canonical transaction ledger — every transaction the platform has classified, whether it resolved to per-document e-invoicing clearance, an aggregate e-reporting period, or no obligation at all. Answers \"what happened to this transaction\" regardless of mechanism.",
        "tags": [
          "Tax Reporting"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "state",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Filter across transaction state, period status, and clearance — accepts NEEDS_INFO, HELD, OPEN, CLOSED, SUBMITTED, FILED, CLEARED, or a raw ledger state value. Case-insensitive."
          },
          {
            "name": "mandate",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Exact match on the resolved Compliance Mandate (e.g. FR_EINVOICING, FR_EREPORTING, NONE)."
          },
          {
            "name": "period",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Exact match on a reporting-period key (e.g. 2026-09-D1 for an FR décade)."
          },
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Tax-point/issue date range start (inclusive)."
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Tax-point/issue date range end (inclusive)."
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Matching ledger rows, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "transactions": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MandateTransaction"
                      }
                    },
                    "pagination": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "integer"
                        },
                        "page": {
                          "type": "integer"
                        },
                        "limit": {
                          "type": "integer"
                        },
                        "pages": {
                          "type": "integer"
                        },
                        "hasNext": {
                          "type": "boolean"
                        },
                        "hasPrev": {
                          "type": "boolean"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed query parameter",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/mandate-transactions/{id}": {
      "get": {
        "operationId": "getMandateTransaction",
        "summary": "Get mandate transaction detail",
        "description": "Full detail for one canonical ledger row — the same envelope as the list endpoint plus `resolutionHistory`: the append-only journey of every Compliance Mandate resolution this transaction has carried (why it was e-invoiced / e-reported / excluded, and what changed since — e.g. a reclassification triggered by a backdated OSS registration, including the linked rectification when the original period was already filed).",
        "tags": [
          "Tax Reporting"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Transaction detail with resolution journey.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "transaction": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/MandateTransaction"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "resolutionHistory": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/MandateResolutionJourneyEntry"
                              },
                              "description": "Oldest first. One entry for most rows; longer when a later event superseded the original resolution."
                            }
                          }
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such transaction for this entity (cross-entity access reads identically to not found)."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/period-reconciliation": {
      "get": {
        "operationId": "exportPeriodReconciliation",
        "summary": "Period reconciliation export",
        "description": "Downloadable reconciliation export (Content-Disposition: attachment): per mandate, per reporting period in the requested range — computed totals derived from the canonical transaction ledger vs. what was actually submitted, the variance, itemized unreconciled rows, and the stored authority/partner receipt references. Everything derives from the same canonical rows used for filing itself — there is no parallel ledger that could drift. For a per-document mandate (no period concept), totals cover the requested date range instead.",
        "tags": [
          "Tax Reporting"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "from",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "mandate",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Restrict to one mandate — omit for every mandate with canonical rows in range."
          }
        ],
        "responses": {
          "200": {
            "description": "The reconciliation export.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "entityId": {
                      "type": "string"
                    },
                    "from": {
                      "type": "string",
                      "format": "date"
                    },
                    "to": {
                      "type": "string",
                      "format": "date"
                    },
                    "heldUnmapped": {
                      "type": "integer",
                      "description": "Count of transactions in range held as HELD_UNMAPPED_DECISION — resolved with no matching compliance-mandate rule at all, so never attributable to any one mandate and therefore reported here rather than inside `mandates`. A non-zero value means the reconciliation below is incomplete until those rows are re-resolved."
                    },
                    "mandates": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "mandate": {
                            "type": "string"
                          },
                          "mandateClass": {
                            "type": "string"
                          },
                          "periods": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "periodKey": {
                                  "type": "string"
                                },
                                "periodStart": {
                                  "type": "string",
                                  "format": "date",
                                  "nullable": true
                                },
                                "periodEnd": {
                                  "type": "string",
                                  "format": "date",
                                  "nullable": true
                                },
                                "periodStatus": {
                                  "type": "string"
                                },
                                "computedTotals": {
                                  "type": "array",
                                  "items": {
                                    "type": "object",
                                    "additionalProperties": true
                                  },
                                  "description": "Per-currency totals computed from the canonical rows."
                                },
                                "submittedTotals": {
                                  "type": "array",
                                  "items": {
                                    "type": "object",
                                    "additionalProperties": true
                                  },
                                  "description": "Per-currency totals actually submitted for the period."
                                },
                                "variance": {
                                  "type": "array",
                                  "items": {
                                    "type": "object",
                                    "additionalProperties": true
                                  }
                                },
                                "unreconciledTransactions": {
                                  "type": "array",
                                  "items": {
                                    "type": "object",
                                    "additionalProperties": true
                                  },
                                  "description": "Itemized rows contributing to the variance."
                                },
                                "receipt": {
                                  "type": "object",
                                  "properties": {
                                    "references": {
                                      "type": "array",
                                      "items": {
                                        "type": "string"
                                      }
                                    },
                                    "latestAttemptStatus": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "submittedAt": {
                                      "type": "string",
                                      "format": "date-time",
                                      "nullable": true
                                    },
                                    "filedAt": {
                                      "type": "string",
                                      "format": "date-time",
                                      "nullable": true
                                    }
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing/malformed from/to, or to before from",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/fr/transactions/needs-info": {
      "get": {
        "operationId": "listFrNeedsInfo",
        "summary": "List NEEDS_INFO remediation queue (FR)",
        "description": "Every FR e-reporting transaction currently parked in NEEDS_INFO (missing a field the mandate requires), with per-row missing-field detail and a server-computed countdown to the affected period's filing deadline. Rows here are NOT yet accumulated — resolve them via PATCH /fr/transactions/needs-info/{id} (or a corrected resubmission under a new x-idempotency-key) before the deadline.",
        "tags": [
          "Tax Reporting"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "transaction_date range start (inclusive)."
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "transaction_date range end (inclusive)."
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The remediation queue.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "transactions": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "One parked row: ids, transaction data, missingFields[], the affected period, its filing deadline, and a deadline countdown."
                      }
                    },
                    "pagination": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "integer"
                        },
                        "page": {
                          "type": "integer"
                        },
                        "limit": {
                          "type": "integer"
                        },
                        "pages": {
                          "type": "integer"
                        },
                        "hasNext": {
                          "type": "boolean"
                        },
                        "hasPrev": {
                          "type": "boolean"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/fr/transactions/needs-info/{id}": {
      "patch": {
        "operationId": "resupplyFrNeedsInfo",
        "summary": "Resupply a missing field (FR NEEDS_INFO)",
        "description": "Completes the NEEDS_INFO remediation loop: patches the missing field on a parked FR e-reporting transaction, re-runs classification and the completeness check, and on success accumulates the row into its ORIGINAL tax-point period (automatically flagging that period for rectification if it was already submitted). An invalid or still-incomplete patch leaves the row parked with a freshly recomputed missing-field/deadline snapshot. Only customerCountry is patchable today — the one field FR e-reporting's required-field spec names.",
        "tags": [
          "Tax Reporting"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "customerCountry"
                ],
                "properties": {
                  "customerCountry": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 2,
                    "description": "ISO 3166-1 alpha-2 — validated with the same rule as CSV ingestion (a country name like \"Germany\" is rejected, never stored)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The row's new state — accumulated into its original period, or still parked with the recomputed missing-field detail.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid customerCountry (must be ISO 3166-1 alpha-2) or malformed body",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such NEEDS_INFO transaction for this entity."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/status": {
      "get": {
        "operationId": "pollStatus",
        "summary": "Poll clearance status",
        "description": "Poll the current status of a document Clearvo has accepted, in any status including RECEIVED and the waiting statuses. Reads the stored status only: it never calls the tax authority, so polling cannot cause a submission. Call repeatedly until `terminal: true`, spacing calls with `nextPollAfter`. `statusClass` says who the document is waiting for (IN_FLIGHT, WAIT_MERCHANT, WAIT_PLATFORM, DONE_OK, DONE_REJECTED, DONE_NA) and `nextStep` says what has to happen and where. To be told instead of polling, subscribe to the `invoice.status_changed` webhook. See https://clearvo.io/docs/e-invoice-lifecycle.",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "referenceId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your invoice reference ID"
          }
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Current status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StatusResponse"
                }
              }
            }
          },
          "404": {
            "description": "Invoice not found for this API key"
          },
          "503": {
            "description": "A database read failure (code DB_ERROR)."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/invoices/{id}": {
      "get": {
        "operationId": "getInvoice",
        "summary": "Get invoice detail",
        "description": "Returns full detail for a single submitted invoice, including `statusClass` and `nextStep`. Accepts the Clearvo invoice ID or the authority reference ID (SDI IdentificativoSdI, KSeF referenceNumber, etc.). Includes all fields from the list endpoint plus events timeline, authority references, upstream error details, suggested action, and submitted XML.",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Invoice ID or authority reference ID"
          }
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Invoice detail",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "invoice": {
                      "$ref": "#/components/schemas/InvoiceDetail"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "404": {
            "description": "Invoice not found or not owned by caller"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "400": {
            "description": "The id path parameter is missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/documents/{id}": {
      "get": {
        "operationId": "getDocument",
        "summary": "Get invoice XML or PDF",
        "description": "Retrieve the submitted document for an invoice. `format=xml` (default) returns the raw authority-format XML (FatturaPA, UBL BIS 3.0, XRechnung, etc.) as submitted. `format=pdf` returns a readable PDF rendered from the invoice's own stored data — available for any invoice in any country except IL/JO/EG on invoices we issued, independent of the customer-notification flow (which is limited to ES/PT/FR/DE/IT-B2C/PL-no-NIP and reaches only the customer, via an emailed link, not the API caller). `format=original` (DE ZUGFeRD records only, sent or received) returns the exact archived original bytes on file — byte-identical to what was actually generated/received, distinct from `format=pdf`'s freshly-rendered copy; GoBD's 8-year retention requirement is what this exists to satisfy. For a received invoice, `format=pdf` returns the archived PDF byte for byte when the supplier sent one; for any other received invoice (for example a Peppol invoice, which arrives as XML) it returns a labelled reader copy rendered from the structured data, marked with the response header `x-clearvo-rendition: generated` — a readable copy, not an invoice in its own right; fetch `format=xml` for the document itself. The same value is exposed as `rendition` on GET /invoices and GET /invoices/{id}. The `Content-Disposition` file name is derived from the stored name and sanitised to a safe ASCII form (with an RFC 8187 `filename*` for non-ASCII names).",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "xml",
                "json",
                "pdf",
                "original"
              ],
              "default": "xml"
            },
            "description": "xml: raw XML (default). json: XML wrapped in a JSON envelope. pdf: a rendered PDF copy of the invoice. original: the exact archived original bytes (DE ZUGFeRD records only)."
          }
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "XML document (default), a PDF when `format=pdf`, or the archived original when `format=original`",
            "content": {
              "application/xml": {
                "schema": {
                  "type": "string"
                }
              },
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "404": {
            "description": "Invoice not found, or (format=original only) no archived original is on file for this record."
          },
          "422": {
            "description": "format=pdf only. The record cannot be drawn as a PDF: body is `{ error, code, hint }` with code PDF_NOT_RENDERABLE (the record has too little stored data — for example a legacy record without invoice lines), PDF_SCRIPT_UNSUPPORTED (the renderer is Latin-script-only and not verified for an invoice we issued in IL/JO/EG, or a received invoice contains characters it has no font for) or PDF_TOO_LARGE (more lines or longer text than the renderer limit). The hint points to format=xml, format=json or format=original instead.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "PDF_NOT_RENDERABLE",
                        "PDF_SCRIPT_UNSUPPORTED",
                        "PDF_TOO_LARGE"
                      ]
                    },
                    "hint": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "x-errorCodes": [
              "PDF_NOT_RENDERABLE",
              "PDF_SCRIPT_UNSUPPORTED",
              "PDF_TOO_LARGE"
            ]
          },
          "500": {
            "description": "format=pdf only. PDF rendering failed; retry shortly."
          },
          "503": {
            "description": "A database read failure (code DB_ERROR)."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/export": {
      "get": {
        "operationId": "exportInvoices",
        "summary": "Bulk export invoices, or export a data-query-tool result set",
        "description": "Two modes on the same endpoint, selected by whether `dataset` is present:\n\n- **Legacy mode** (no `dataset`): export invoices in CSV or JSONL format using `from`/`to`/`countries`/`statuses`/`limit`. Maximum 10,000 records per request. Each record includes the same fields as InvoiceListItem, including clearanceStatus and the ready-to-render clearanceStatusLabel.\n- **Canonical-filter mode** (`dataset` present): file-export counterpart to POST /query — same `filters`/`columns`/`from`/`to` shape, same field/operator/enum validation, same COST_REJECTED and rate-limit rules, so a filter built for /query can be reused verbatim for an export. Output is `csv` or `xlsx` (not jsonl). Capped at 10,000 rows and rate-limited in its own budget, separate from /query's. Every attempt is audit-logged the same as a live query. Both modes accept a read-only key.",
        "tags": [
          "E-Invoicing",
          "Data Query Tool"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "csv",
                "jsonl",
                "xlsx"
              ],
              "default": "csv"
            },
            "description": "jsonl is legacy-mode only; xlsx is canonical-filter-mode only."
          },
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Legacy mode: invoice date. Canonical mode: inclusive lower bound on the dataset's canonical timestamp field."
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Same field as `from`, upper bound."
          },
          {
            "name": "countries",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Legacy mode only. Comma-separated list of ISO country codes"
          },
          {
            "name": "statuses",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Legacy mode only. Comma-separated list of clearance statuses"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1000,
              "maximum": 10000
            }
          },
          {
            "name": "dataset",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "einvoicing_records",
                "tax_calculations",
                "tax_calculation_line_items"
              ]
            },
            "description": "Presence of this param switches the route into canonical-filter mode. See GET /query/fields for each dataset's field/operator/enum allowlist."
          },
          {
            "name": "filters",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Canonical-filter mode only. JSON-encoded array of QueryToolFilter, e.g. [{\"field\":\"country\",\"operator\":\"eq\",\"value\":\"DE\"}]."
          },
          {
            "name": "columns",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Canonical-filter mode only. JSON array or comma-separated list of field names, e.g. id,country,totalNet."
          },
          {
            "name": "includeOriginals",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": false
            },
            "description": "Legacy mode only. \"true\" appends four GoBD-archive columns/fields (documentFormat, originalMimeType, originalSizeBytes, originalSha256) so a reconciliation export can tell which rows have a byte-faithful original on file — fetch it via GET /v1/documents/{id}?format=original. Omitted (default), columns are unchanged — an existing integrator parsing this CSV by position never sees new columns unless it asks for them."
          }
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "File download",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              },
              "application/x-ndjson": {
                "schema": {
                  "type": "string"
                }
              },
              "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "description": "Canonical-filter mode only. Malformed filters/columns/format, an unlisted field/operator/enum value, or a from/to span wider than the dataset's maxSpanDays.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QueryToolErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Canonical-filter mode only. Well-formed request that would force an expensive scan (no indexed predicate, or too wide a date span).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QueryToolErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Canonical-filter mode only. Too many export requests for this API key this minute.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QueryToolThrottleErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected failure running the export.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": false
                    },
                    "error": {
                      "type": "object",
                      "properties": {
                        "code": {
                          "type": "string",
                          "enum": [
                            "INTERNAL_ERROR"
                          ]
                        },
                        "message": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/query/fields": {
      "get": {
        "operationId": "getQueryToolSchema",
        "summary": "Get the data query tool's filterable fields, operators, and enum values",
        "description": "Discoverable, machine-readable schema for POST /query's filter/column surface — which fields each dataset can be filtered/selected on, with which operators, enum values, and index-backed status. Always call this rather than hardcoding a field list — a new filterable field is added here first, and validation on POST /query is the sole source of truth for what a request actually may do.",
        "tags": [
          "Data Query Tool"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QueryToolSchemaResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/query": {
      "post": {
        "operationId": "queryData",
        "summary": "Run a filtered, paginated query over einvoicing_records, tax_calculations, or tax_calculation_line_items",
        "description": "Generic filtered/paginated query over the datasets the data query tool covers. Field/operator/enum allowlists per dataset come from GET /query/fields — never hardcode them. Every outcome (executed, rejected, cost-rejected, throttled) is durably audit-logged before the route resolves. Rate limited per authenticated API key in its own budget (the 429 below), separate from the per-key request limits that apply to the rest of the API — calls here are not counted against those (see the Rate limits section of the API overview). Read access is enough — this is the public equivalent of the dashboard's Explore tool, available to every member role.",
        "tags": [
          "Data Query Tool"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "dataset"
                ],
                "properties": {
                  "dataset": {
                    "type": "string",
                    "enum": [
                      "einvoicing_records",
                      "tax_calculations",
                      "tax_calculation_line_items"
                    ]
                  },
                  "filters": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/QueryToolFilter"
                    }
                  },
                  "columns": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Defaults to the dataset's defaultColumns (see GET /query/fields) when omitted."
                  },
                  "limit": {
                    "type": "integer",
                    "description": "Defaults to the dataset's defaultPageSize; capped at maxPageSize."
                  },
                  "from": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Inclusive lower bound on the dataset's canonical timestamp field."
                  },
                  "to": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Inclusive upper bound."
                  },
                  "sort": {
                    "type": "object",
                    "description": "Optional sort override. `field` must be `sortable: true` in GET /query/fields — indexed AND non-nullable. Omit for the dataset's implicit default (its canonical timestamp field, descending). A cursor issued under one sort cannot be replayed under a different one.",
                    "properties": {
                      "field": {
                        "type": "string",
                        "example": "country"
                      },
                      "direction": {
                        "type": "string",
                        "enum": [
                          "asc",
                          "desc"
                        ]
                      }
                    },
                    "required": [
                      "field",
                      "direction"
                    ]
                  },
                  "cursor": {
                    "type": "string",
                    "description": "Opaque keyset cursor from a previous response's nextCursor. Omit for the first page."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QueryToolQueryResponse"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request, an invalid/expired cursor, or an unlisted field/operator/enum value.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QueryToolErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity, or lacks the product scope this dataset requires.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QueryToolErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "Well-formed request that would force an expensive scan — no indexed predicate at all, or a from/to span wider than the dataset's maxSpanDays.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QueryToolErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many query-tool requests for this API key this minute.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QueryToolThrottleErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected failure running the query.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": false
                    },
                    "error": {
                      "type": "object",
                      "properties": {
                        "code": {
                          "type": "string",
                          "enum": [
                            "INTERNAL_ERROR"
                          ]
                        },
                        "message": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/invoices/{id}/deliver": {
      "post": {
        "operationId": "retryDelivery",
        "summary": "Retry invoice delivery",
        "description": "Retry delivery for an invoice with UNROUTABLE or UNDELIVERED status. Triggers a fresh Peppol SMP lookup and AS4 delivery attempt.",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "electronicAddress": {
                    "type": "object",
                    "description": "Override the customer's stored or derived Peppol electronic address for this retry.",
                    "properties": {
                      "value": {
                        "type": "string",
                        "description": "Peppol participant ID for the scheme in `schemeId`."
                      },
                      "schemeId": {
                        "type": "string",
                        "description": "Peppol EAS scheme ID."
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Delivery retry accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StatusResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invoice is not in a retryable state"
          },
          "404": {
            "description": "Invoice not found"
          },
          "503": {
            "description": "Delivery could not be queued (code QUEUE_FAILED) — safe to retry."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/invoices/{id}/retry": {
      "post": {
        "operationId": "retryInvoice",
        "summary": "Retry an undelivered invoice",
        "description": "Retries a previously UNDELIVERED invoice by resubmitting its stored XML directly to the authority. Only supported when clearanceStatus is UNDELIVERED (the authority never received the submission — typically a network failure). A REJECTED invoice cannot be retried here — fix the underlying issue and submit a new invoice via POST /send. Supported countries: IT (SDI), PL (KSeF), RO (ANAF), HU (NAV). A 60-second cooldown is enforced between retry attempts on the same invoice. This is the public-API counterpart of the dashboard's retry action. If the authority rejects the retried document, the answer is 200 with `ok: false`, `clearanceStatus: REJECTED`, `outcome: REJECTED` and `terminal: true`: read the body, not the status code.",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Invoice ID or authority reference ID"
          }
        ],
        "responses": {
          "200": {
            "description": "The retry was processed. Check `clearanceStatus`: PENDING or ACCEPTED when the authority took the resubmission, or REJECTED (with `ok: false` and `terminal: true`) when the authority rejected the retried document.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true,
                      "description": "`false` when the authority rejected the retried document."
                    },
                    "clearanceStatus": {
                      "type": "string",
                      "enum": [
                        "PENDING",
                        "ACCEPTED",
                        "REJECTED"
                      ]
                    },
                    "referenceId": {
                      "type": "string",
                      "nullable": true
                    },
                    "ksefNumber": {
                      "type": "string",
                      "nullable": true,
                      "description": "Present only for PL retries where KSeF returned an immediate number."
                    },
                    "outcome": {
                      "type": "string",
                      "enum": [
                        "CLEARED",
                        "REJECTED"
                      ]
                    },
                    "terminal": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing id path parameter",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Invoice not found, or not owned by the caller",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Nothing was retried and the invoice is unchanged: it is not retryable (wrong clearanceStatus), has no stored XML, is for an unsupported country, or the entity credentials are missing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Retried too soon — a 60-second cooldown is enforced between retry attempts on the same invoice. The wait time in seconds is returned both as retryAfter in the body and as the Retry-After response header. Also returned, with the RateLimited envelope (`error.code` RATE_LIMIT_EXCEEDED) instead of this body, when this API key's per-minute request limit is exceeded — see the Rate limits section of the API overview.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "retryAfter": {
                          "type": "integer",
                          "description": "Seconds to wait before retrying."
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "502": {
            "description": "The authority is temporarily unavailable — safe to retry later",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/invoices/{id}/amend-report": {
      "post": {
        "operationId": "amendSiiReport",
        "summary": "File an AEAT Spain SII A1 amendment",
        "description": "Spain SII block 3. Files an AEAT \"A1\" amendment against an already-registered SII invoice — a COMPLETE re-registration of the document's content, submitted in the identical shape as POST /send (same invoiceNumber/issueDate as the original; A1 corrects CONTENT of an already-registered registro, it is never a rectificativa and never changes the invoice number). Never falls back to a fresh A0 — every refusal below is terminal for this request. This route only builds and enqueues the amendment; it never calls AEAT directly (same P1/P2 split /send itself uses for Spain). On success the record's own `clearanceStatus` stays ACCEPTED — only the AEAT communication ledger gains a new entry, and any accepted-with-errors flag/admissible-error list is cleared once AEAT answers Correcto.",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKeyRequired"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The invoice ID (or referenceId) whose SII registration to amend."
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Identical shape to POST /send's own InvoiceRequest (the full corrected invoice) — the IDFactura identity fields (supplier's registered NIF, invoiceNumber, issueDate) must exactly match the original registro.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InvoiceRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Accepted for transmission. `id` is the SAME record id as the original A0 — an amendment never creates a new record.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "id",
                    "operation",
                    "communicationId",
                    "state"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    },
                    "id": {
                      "type": "string"
                    },
                    "operation": {
                      "type": "string",
                      "enum": [
                        "A1"
                      ]
                    },
                    "communicationId": {
                      "type": "string",
                      "description": "The new AEAT communication ledger row id — distinct per attempt, so a full A0→A1→A1 history is always reconstructible."
                    },
                    "state": {
                      "type": "string",
                      "description": "The communication's own lifecycle state (e.g. PENDING until AEAT's real answer comes back)."
                    },
                    "idempotentReplay": {
                      "type": "boolean",
                      "description": "true only when this response is a same-idempotency-key replay of an already-recorded attempt, not a fresh one."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing/invalid id or x-idempotency-key, or a structural validation error in the corrected invoice.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Invoice not found, or not owned by the caller",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "NOT_AN_SII_RECORD | SII_NOT_ACCEPTED_AT_AEAT (the record isn't currently Correcto/AceptadoConErrores) | SII_OPERATION_IN_FLIGHT (an earlier amend/cancel is still awaiting AEAT's answer) | SII_BATCH_IN_FLIGHT | AMEND_IS_RECTIFICATIVA | AMEND_IDENTITY_CHANGE_NOT_ALLOWED (the corrected payload would change the IDFactura identity).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "OBLIGATION_DISABLED | OBLIGATION_NOT_EFFECTIVE | a classification/build failure in the corrected invoice.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/invoices/{id}/cancel-report": {
      "post": {
        "operationId": "cancelSiiReport",
        "summary": "File an AEAT Spain SII Baja (withdrawal)",
        "description": "Spain SII block 3. Withdraws an already-registered SII invoice's AEAT registration — identity-only (no invoice content is sent; built from the persisted IDFactura + PeriodoLiquidacion). This withdraws the SII REGISTRATION, not the invoice itself — if the invoice is wrong or was refunded, issue a credit note via POST /send instead. Idempotent: cancelling an already-cancelled invoice returns 200 with its stored `cancelledAt` rather than erroring, regardless of the x-idempotency-key presented. On success `clearanceStatus` moves to the terminal CANCELLED status; a subsequent POST /send with the identical IDFactura tuple is then refused.",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKeyRequired"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The invoice ID (or referenceId) whose SII registration to cancel."
          }
        ],
        "requestBody": {
          "required": false,
          "description": "Identity-only — no other field is accepted (400 UNSUPPORTED_FIELD otherwise).",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reason": {
                    "type": "string",
                    "maxLength": 100,
                    "description": "Optional free text (<=100 chars) — recorded for audit on the actor-event trail; never sent to AEAT (Baja's own envelope is identity-only)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A fresh Baja was filed, OR the invoice was already cancelled (idempotent no-op, no error).",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "required": [
                        "ok",
                        "id",
                        "operation",
                        "communicationId",
                        "state"
                      ],
                      "properties": {
                        "ok": {
                          "type": "boolean",
                          "example": true
                        },
                        "id": {
                          "type": "string"
                        },
                        "operation": {
                          "type": "string",
                          "enum": [
                            "BAJA"
                          ]
                        },
                        "communicationId": {
                          "type": "string"
                        },
                        "state": {
                          "type": "string"
                        },
                        "idempotentReplay": {
                          "type": "boolean"
                        }
                      }
                    },
                    {
                      "type": "object",
                      "description": "The idempotent no-op returned when the target invoice's SII registration was already withdrawn — regardless of the x-idempotency-key presented.",
                      "required": [
                        "ok",
                        "id",
                        "cancelled",
                        "cancelledAt"
                      ],
                      "properties": {
                        "ok": {
                          "type": "boolean",
                          "example": true
                        },
                        "id": {
                          "type": "string"
                        },
                        "cancelled": {
                          "type": "boolean",
                          "example": true
                        },
                        "cancelledAt": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Missing/invalid id or x-idempotency-key, invalid JSON, an unrecognised body field, or an invalid `reason` value.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Invoice not found, or not owned by the caller",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "NOT_AN_SII_RECORD | SII_NOT_REGISTERED_AT_AEAT | SII_OPERATION_IN_FLIGHT | SII_BATCH_IN_FLIGHT",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "OBLIGATION_DISABLED | OBLIGATION_NOT_EFFECTIVE | ES_NIF_NOT_CONFIGURED | a Baja envelope build failure",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/invoices/{id}/business-status": {
      "patch": {
        "operationId": "updateInvoiceBusinessStatus",
        "summary": "Record a customer-lifecycle status on a received invoice",
        "description": "Records your business decision on an invoice you received (direction=\"inbound\"). The one shared route every registered regime goes through — currently France (delivered through an accredited third-party platform while Clearvo's own PA accreditation is processed), Germany, and Brazil (Manifestação do Destinatário — Ciência/Confirmação/Operação-não-Realizada/Desconhecimento). Clearvo validates the transition against the invoice's own country/regime vocabulary before pushing it to the network or authority — an invalid transition, or a value from a different regime's vocabulary, never reaches the authority side at all. FR/DE: approve, partially approve, dispute, suspend, refuse, or mark paid. Brazil: acknowledge receipt (CIENCIA), confirm the operation occurred (CONFIRMACAO), or reject it — either the operation didn't occur (OPERACAO_NAO_REALIZADA, rejectionDetail.message required, 15–255 characters) or you don't recognize the transaction (DESCONHECIMENTO, rejectionDetail.message forbidden). PRESUMIDA (Brazil's day-90 deemed-acceptance presumption) is a real, readable value but is never a valid PATCH target — SEFAZ's own statutory silence writes it, not a customer request. Get the invoice's own currently-available next steps from GET /v1/invoices/{id}'s businessStatusActions array first — never guess from this enum alone. For an invoice you issued, the customer's own decision syncs onto this same field automatically — there is nothing to call for that direction.",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Invoice ID or authority reference ID"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "IN_HAND",
                      "APPROVED",
                      "PARTIALLY_APPROVED",
                      "DISPUTED",
                      "SUSPENDED",
                      "REFUSED",
                      "COMPLETED",
                      "PAYMENT_SENT",
                      "PAYMENT_RECEIVED",
                      "CIENCIA",
                      "CONFIRMACAO",
                      "OPERACAO_NAO_REALIZADA",
                      "DESCONHECIMENTO"
                    ],
                    "description": "The union of every registered regime's own native vocabulary — only the values belonging to the invoice's own country/regime are ever a valid transition for it. FR/DE values come from the underlying mandate's own lifecycle model; CIENCIA/CONFIRMACAO/OPERACAO_NAO_REALIZADA/DESCONHECIMENTO are Brazil's own Manifestação do Destinatário vocabulary."
                  },
                  "rejectionDetail": {
                    "description": "Required when status is DISPUTED, SUSPENDED, or REFUSED (FR/DE — reason required), or OPERACAO_NAO_REALIZADA (Brazil — message required, 15–255 characters; reason accepted but never required). Forbidden for Brazil's DESCONHECIMENTO.",
                    "oneOf": [
                      {
                        "type": "object",
                        "properties": {
                          "reason": {
                            "type": "string"
                          },
                          "message": {
                            "type": "string"
                          }
                        }
                      },
                      {
                        "type": "string"
                      }
                    ]
                  }
                },
                "required": [
                  "status"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Status recorded",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    },
                    "businessStatus": {
                      "type": "string",
                      "description": "The invoice's new native status value."
                    },
                    "canonicalBusinessStatus": {
                      "type": "string",
                      "enum": [
                        "RECEIVED",
                        "ACKNOWLEDGED",
                        "ACCEPTED",
                        "DISPUTED"
                      ],
                      "description": "The shared, regime-agnostic bucket every registered regime's native value maps onto. Brazil: CIENCIA→ACKNOWLEDGED, CONFIRMACAO/PRESUMIDA→ACCEPTED, OPERACAO_NAO_REALIZADA/DESCONHECIMENTO→DISPUTED."
                    },
                    "updatedAt": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing/invalid body, the invoice's country has no registered business-status regime, the invoice is not direction=\"inbound\", the status value isn't a member of this invoice's own regime vocabulary, or, for Brazil, a missing/too-short/too-long justification (XJUST_REQUIRED / XJUST_TOO_SHORT / XJUST_TOO_LONG, 15–255 characters) or one supplied where forbidden (XJUST_FORBIDDEN).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Invoice not found, or not owned by the caller",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "The requested status is not a valid transition from the invoice's current business status (INVALID_TRANSITION). Also returned (Error.code=CONCURRENT_UPDATE) when a concurrent request already changed it first — refresh and check the current status rather than retrying. Brazil specifically: no e-CNPJ certificate is on file (BR_CREDENTIAL_MISSING), the certificate has expired (BR_CREDENTIAL_EXPIRED), a production certificate's homologação run hasn't completed yet (BR_NOT_HOMOLOGATED — register directly with SEFAZ/your ERP instead; the deadline is not paused), or SEFAZ itself rejected the event (SEFAZ_REJECTED — the response's error is SEFAZ's own xMotivo text verbatim).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "The status was validated but the network/authority call failed — the status was NOT changed, safe to retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Brazil only (RATE_LIMITED) — SEFAZ's own shared 20/hour CNPJ-root rate limit was exceeded while registering this event; the status was NOT changed. Retry in about an hour.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/invoices/{id}/resend-notification": {
      "post": {
        "operationId": "resendInvoiceNotification",
        "summary": "Resend the customer invoice notification email",
        "description": "Re-fires the customer invoice notification email (see customerNotification on POST /send) against an already-submitted invoice — for when the original send was skipped (NOT_OPTED_IN / MISSING_CUSTOMER_EMAIL) or failed, or the caller just wants to resend the hosted view/download link. Unlike the original send-time attempt, this always forces a send regardless of the entity's notifyCustomerByDefault setting. This is the public-API counterpart of the dashboard's resend-notification action.",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Invoice ID or authority reference ID"
          }
        ],
        "responses": {
          "200": {
            "description": "Notification re-send attempted — check customerNotification.status for the actual outcome (SENT/SKIPPED/FAILED); a SKIPPED or FAILED result still returns 200, since the resend attempt itself completed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    },
                    "customerNotification": {
                      "type": "object",
                      "properties": {
                        "status": {
                          "type": "string",
                          "enum": [
                            "SENT",
                            "SKIPPED",
                            "FAILED"
                          ]
                        },
                        "reason": {
                          "type": "string",
                          "enum": [
                            "NOT_APPLICABLE",
                            "NOT_OPTED_IN",
                            "MISSING_CUSTOMER_EMAIL",
                            "NOTIFICATIONS_NOT_CONFIGURED"
                          ],
                          "description": "Present only when status is SKIPPED."
                        },
                        "error": {
                          "type": "string",
                          "description": "Present only when status is FAILED."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing id path parameter",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Invoice not found, or not owned by the caller",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Not eligible for a resend — either clearanceStatus is not ACCEPTED/DELIVERED (a REJECTED or otherwise never-actually-issued invoice never legally existed, so no notification may be sent for it), or no stored invoice data was found for this record.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/invoices/resend-notification/batch": {
      "post": {
        "operationId": "resendInvoiceNotificationBatch",
        "summary": "Resend the customer invoice notification email for multiple invoices",
        "description": "Batch counterpart of POST /invoices/{id}/resend-notification — re-fires the customer invoice notification email for up to 100 already-submitted invoices in one call. Each id is processed independently; a per-id failure (not found, not eligible, no stored invoice data, send failed) never fails the rest of the batch. There is no dashboard bulk-select UI for this — it exists for API integrators only. Rate-limited more tightly than the single-invoice endpoint — 60 requests per minute per API key, the heavy-operations class in the Rate limits section of the API overview — since each call can fan out to up to 100 real emails.",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "ids"
                ],
                "properties": {
                  "ids": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 100,
                    "items": {
                      "type": "string"
                    },
                    "description": "Invoice ID or authority reference ID per entry. De-duplicated server-side."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Batch processed — check each result's own ok/status for its actual outcome.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "ok": {
                            "type": "boolean"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "SENT",
                              "SKIPPED",
                              "FAILED"
                            ]
                          },
                          "reason": {
                            "type": "string",
                            "description": "Present when the item was skipped or not eligible."
                          },
                          "error": {
                            "type": "string",
                            "enum": [
                              "NOT_FOUND",
                              "NOT_ELIGIBLE",
                              "NO_INVOICE_DATA",
                              "INTERNAL_ERROR"
                            ],
                            "description": "Present only when ok is false and the failure wasn't a SKIPPED notification outcome."
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed body — ids missing, empty, not an array, or over the 100-item cap",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/webhooks/test": {
      "post": {
        "operationId": "testWebhook",
        "summary": "Send a test event to a webhook",
        "tags": [
          "Webhooks"
        ],
        "description": "Sends one signed test event to a webhook you registered, so you can check that your endpoint is reachable, verifies the signature and returns a 2xx. The request is built and signed exactly like a real delivery (x-taxually-signature, x-taxually-timestamp, x-taxually-webhook-id, x-taxually-delivery-id) and the JSON body carries \"test\": true. It is sent once, with no retries, and nothing is stored; no real invoice changes. The response always reports what your endpoint did: a receiver that fails or times out is still a 200 here with delivered false and an error, so you can tell that apart from a bad request. Pass invoiceId to build the payload from one of your own invoices (for example an inbound invoice, to test invoice.received) instead of sample values. To test the general status-change event, send event `invoice.status_changed` and, optionally, `to` (a status such as NEEDS_INFO) to see the payload for that status; the payload carries \"test\": true. If the webhook would not receive a real event of that kind (not subscribed, or the status is not in its `statuses`), the response carries `subscribed: false` and a `note` saying why. Like the other webhook endpoints, x-entity-id is not used.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookTestRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The test event was sent. Check delivered, status and error for what your endpoint answered.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookTestResult"
                }
              }
            }
          },
          "400": {
            "description": "Invalid JSON, a missing webhookId, an event that cannot be simulated, a `to` that is not a status or is sent with another event, an invoiceId for a different entity than the webhook's, or an invoiceId that is not a string.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "The webhook (or the invoice, when invoiceId is given) was not found for this API key, or the webhook is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/webhooks": {
      "description": "Unlike most /v1/* endpoints, x-entity-id is not used here and has no effect for any of the three operations below — a webhook is account-wide (delivered for every entity in the organisation) by default. An entity-restricted organisation-scoped key targets one of its allowed entities via the entityId field in the POST request body instead (see WebhookEndpoint); GET and DELETE already only ever see/act on webhooks that key is authorized for.",
      "get": {
        "operationId": "listWebhooks",
        "summary": "List webhook endpoints",
        "tags": [
          "Webhooks"
        ],
        "responses": {
          "200": {
            "description": "List of registered endpoints",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "webhooks",
                    "pagination"
                  ],
                  "properties": {
                    "webhooks": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookEndpoint"
                      }
                    },
                    "pagination": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "integer"
                        },
                        "page": {
                          "type": "integer"
                        },
                        "limit": {
                          "type": "integer"
                        },
                        "pages": {
                          "type": "integer"
                        },
                        "hasNext": {
                          "type": "boolean"
                        },
                        "hasPrev": {
                          "type": "boolean"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            },
            "description": "1-based page number."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            },
            "description": "Rows per page."
          }
        ],
        "description": "Lists the active webhooks visible to the key, newest first."
      },
      "post": {
        "operationId": "createWebhook",
        "summary": "Register a webhook endpoint",
        "tags": [
          "Webhooks"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookCreateRequest"
              }
            }
          }
        },
        "responses": {
          "403": {
            "description": "The API key is restricted to a subset of entities and entityId is missing from the request body, or names an entity outside that allowlist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "201": {
            "description": "Webhook registered. The response includes the signing secret, shown only this once.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookCreated"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "400": {
            "description": "Invalid JSON, a missing url, a url that is not HTTPS, carries credentials, or points at an internal or private address (including a name that resolves to one), or an events value not in the accepted set.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "description": "Registers an HTTPS endpoint to receive webhook deliveries. The url must be on a public address: no credentials in the URL, and no localhost, internal or private addresses (a domain name that resolves to one is refused too). Deliveries are only sent to a public address and redirects are not followed. The signing secret is generated by the server and returned once in the 201 response. Subscribe to `invoice.status_changed` to be told about every status change and choose which statuses you receive with `statuses` (omit or send an empty list for all). `*` and the default events do not include `invoice.status_changed`: list it by name. An unknown status, or `statuses` without `invoice.status_changed` in `events`, is a 400. Partner tenant-wide webhooks take the same `statuses` field. The specific events (invoice.accepted, invoice.rejected and the rest) keep working with their existing payloads; if you subscribe to both, one change produces two deliveries with different delivery ids. Reminders about documents waiting for you are email and dashboard only, not webhooks. See https://clearvo.io/docs/e-invoice-lifecycle#learn-status."
      },
      "delete": {
        "operationId": "deleteWebhook",
        "summary": "Deactivate a webhook endpoint",
        "description": "Deactivates (soft-deletes) the webhook — sets it inactive rather than removing the row. Identify the webhook via the `id` query parameter, not a path segment.",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The webhook's id, from GET /webhooks or the id returned at registration."
          }
        ],
        "responses": {
          "400": {
            "description": "Missing the required id query parameter.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Webhook deactivated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "id": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Webhook not found, already inactive, or belongs to a different entity/organisation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/participants/lookup": {
      "get": {
        "operationId": "lookupParticipant",
        "summary": "Look up a Peppol participant",
        "description": "Resolve a customer's Peppol AS4 endpoint from the SML/SMP network. Uses a cache; results are valid for 24 hours.",
        "tags": [
          "Peppol"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "taxId",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Customer's tax ID (used for auto-derivation in supported countries)"
          },
          {
            "name": "electronicAddress",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Explicit Peppol participant ID (e.g. 0208:0123456789)"
          },
          {
            "name": "electronicAddressSchemeId",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Peppol EAS scheme ID (e.g. 0208)"
          },
          {
            "name": "country",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Customer's country code (required for auto-derivation)"
          },
          {
            "name": "selfBilling",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": false
            },
            "description": "Set true to check registration for the self-billing document type instead of regular billing. AU/NZ use their own PINT-AUNZ self-billing type; every country except SG/JP uses the generic EU BIS Self-Billing 3.0 type; ignored (falls back to regular billing) for SG/JP."
          }
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Participant found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "participantId": {
                      "type": "string"
                    },
                    "endpointUrl": {
                      "type": "string"
                    },
                    "transportProfile": {
                      "type": "string"
                    },
                    "cached": {
                      "type": "boolean"
                    },
                    "derivedFrom": {
                      "type": "string",
                      "nullable": true,
                      "enum": [
                        "vatNumber",
                        "electronicAddress"
                      ],
                      "description": "How the participant ID was resolved."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Participant not found in SML"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tax-numbers/validate": {
      "post": {
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "operationId": "validateTaxNumber",
        "summary": "Validate a tax number",
        "description": "Validate a single VAT number, company registration number, or national tax ID. Supports 100+ countries with real-time authority checks (VIES, HMRC, Brreg, ABN, etc.) for supported countries.",
        "tags": [
          "TIN Validation"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TaxNumberValidateRequest"
              }
            }
          }
        },
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Validation result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaxNumberValidateResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request — taxNumber missing, countryCode missing and not derivable from a prefix on taxNumber, invalid registryType, or malformed JSON.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tax-numbers/validate-batch": {
      "post": {
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "operationId": "validateTaxNumberBatch",
        "summary": "Validate multiple tax numbers",
        "description": "Validate up to 20 tax numbers in a single request. Results include individual validation outcomes with the same detail as the single-validate endpoint.",
        "tags": [
          "TIN Validation"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "items"
                ],
                "properties": {
                  "items": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 20,
                    "items": {
                      "$ref": "#/components/schemas/TaxNumberValidateRequest"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Batch validation results",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "results": {
                      "type": "array",
                      "description": "One entry per request item, in order. An item that could not be validated still appears, with valid=false and an `error` message.",
                      "items": {
                        "allOf": [
                          {
                            "$ref": "#/components/schemas/TaxNumberValidateResponse"
                          },
                          {
                            "type": "object",
                            "required": [
                              "index"
                            ],
                            "properties": {
                              "index": {
                                "type": "integer",
                                "description": "Zero-based position of this item in the request."
                              },
                              "error": {
                                "type": "string",
                                "description": "Present only when this item failed to validate."
                              }
                            }
                          }
                        ]
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "valid": {
                      "type": "integer"
                    },
                    "invalid": {
                      "type": "integer"
                    },
                    "errors": {
                      "type": "integer",
                      "description": "Items that failed to validate (also counted in neither valid nor invalid)."
                    },
                    "sandbox": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "400": {
            "description": "Invalid request — items missing, empty, not an array, or more than 20 entries.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/tax-numbers/validate-batch/async": {
      "post": {
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "operationId": "validateTaxNumberBatchAsync",
        "summary": "Validate large batches of tax numbers (async)",
        "description": "Enqueue up to 1,000 tax numbers for background validation. Returns a `jobId` immediately (HTTP 202). Poll `GET /v1/tax-numbers/validate-batch/{jobId}` for live progress — results are returned incrementally as each item completes. Per-account daily quota: 10,000 items (all entities pooled).",
        "tags": [
          "TIN Validation"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "items"
                ],
                "properties": {
                  "items": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/TaxNumberValidateRequest"
                    },
                    "maxItems": 1000
                  }
                }
              }
            }
          }
        },
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "202": {
            "description": "Job accepted — poll the returned jobId for results",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "jobId": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "total": {
                      "type": "integer"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "pending"
                      ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Daily quota exceeded. Also returned, with the RateLimited envelope (`error.code` RATE_LIMIT_EXCEEDED) instead of this body, when this API key's per-minute request limit is exceeded — see the Rate limits section of the API overview.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "remaining": {
                      "type": "integer",
                      "description": "Items remaining in today's quota"
                    },
                    "limit": {
                      "type": "integer",
                      "description": "Total daily quota"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/tax-numbers/validate-batch/{jobId}": {
      "get": {
        "operationId": "getBatchValidationJob",
        "summary": "Poll async batch validation progress",
        "description": "Returns the current status and all results so far for an async batch job. Safe to poll every few seconds. Results appear incrementally as items complete — `completed` increases from 0 to `total`.",
        "tags": [
          "TIN Validation"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "jobId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Job status and results",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "jobId": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "pending",
                        "processing",
                        "completed",
                        "failed"
                      ]
                    },
                    "total": {
                      "type": "integer",
                      "description": "Total items submitted"
                    },
                    "completed": {
                      "type": "integer",
                      "description": "Items checked so far"
                    },
                    "passed": {
                      "type": "integer",
                      "description": "Items with valid format and authority"
                    },
                    "failed": {
                      "type": "integer",
                      "description": "Items that are invalid or errored"
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TaxNumberValidateResponse"
                      },
                      "description": "Partial results, grows as items complete. Each result includes an `index` field matching its position in the original items array."
                    },
                    "sandbox": {
                      "type": "boolean"
                    },
                    "createdAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "completedAt": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Job not found or does not belong to this API key"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tax-numbers/lookups": {
      "get": {
        "operationId": "listTaxNumberLookups",
        "summary": "List TIN validation history",
        "description": "Paginated history of tax number validations performed with this API key, including cached results.",
        "tags": [
          "TIN Validation"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "countryCode",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 200
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Lookup history",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "lookups": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TaxNumberValidateResponse"
                      }
                    },
                    "cursor": {
                      "type": "string",
                      "nullable": true
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/lookup": {
      "get": {
        "operationId": "lookupCompany",
        "summary": "Company name lookup",
        "description": "Search a national business registry by company name and return matching entities with their tax numbers. Supported countries: Poland (PL — KRS + REGON), Norway (NO — Brønnøysundregistrene, taxNumber is the organisation number) and France (FR — SIRENE, taxNumber is the SIREN).",
        "tags": [
          "TIN Validation"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "country",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "PL",
                "NO",
                "FR"
              ]
            },
            "description": "ISO 3166-1 alpha-2 country code. One of PL, NO, FR."
          },
          {
            "name": "name",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 2
            },
            "description": "Company name to search (at least 2 characters)."
          },
          {
            "name": "city",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Optional — filter results to this city."
          }
        ],
        "responses": {
          "200": {
            "description": "Lookup results",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "country": {
                      "type": "string"
                    },
                    "query": {
                      "type": "string"
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "taxNumber": {
                            "type": "string"
                          },
                          "address": {
                            "type": "string"
                          },
                          "entityType": {
                            "type": "string"
                          },
                          "source": {
                            "type": "string",
                            "description": "Registry the match came from, e.g. \"krs\", \"regon\", \"brreg\", \"sirene\"."
                          },
                          "confidence": {
                            "type": "number",
                            "description": "Match confidence between 0 and 1."
                          },
                          "krsNumber": {
                            "type": "string",
                            "description": "KRS number (PL krs source only)."
                          },
                          "regon": {
                            "type": "string",
                            "description": "REGON number (PL regon source only)."
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing or invalid query parameters, or unsupported country",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tax/registrations": {
      "get": {
        "operationId": "listRegistrations",
        "summary": "List tax registrations",
        "description": "List all tax registrations for the entity, including obligation status, collection status, and threshold data. Organisation-scoped keys must supply the `entityId` query parameter.",
        "tags": [
          "Tax Registrations"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "OAuthBearer": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "entityId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Required when using an organisation-scoped API key."
          }
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Registration list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "entityName": {
                      "type": "string"
                    },
                    "homeCountry": {
                      "type": "string"
                    },
                    "iossNumber": {
                      "type": "string",
                      "nullable": true
                    },
                    "taxCalcEnabled": {
                      "type": "boolean"
                    },
                    "taxCalcSetupRequired": {
                      "type": "boolean"
                    },
                    "monitoring": {
                      "type": "object",
                      "required": [
                        "enabled"
                      ],
                      "description": "Whether Compliance Radar is monitoring thresholds. `enabled: false` means it is switched off for the account (an Enterprise administrator choice): every row has `obligationStatus`, `threshold` and `currentPeriodAmount` null and `obligationStatusLabel` \"Not monitored\", and Radar-created Not Registered monitoring rows are omitted. Stored data is unchanged.",
                      "properties": {
                        "enabled": {
                          "type": "boolean"
                        },
                        "message": {
                          "type": "string",
                          "description": "Present when `enabled` is false."
                        }
                      }
                    },
                    "registrations": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/RegistrationItem"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "entityId required for organisation-scoped key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Entity not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "operationId": "addRegistration",
        "summary": "Add a tax registration",
        "description": "Record a new tax registration for the entity. Works with an entity-scoped key, or an organisation-scoped key plus the `x-entity-id` header. Creates a `tax_registrations` row automatically. Use `region`/`locality` for sub-national registrations (e.g. US state or home-rule locality). Pass `collectFromDate` to activate collection in this same call — a registration with no collection date set will not apply tax for that jurisdiction until PATCH .../collection is called separately. PATCH the existing registration (by `obligationId`) instead of re-POSTing for the same country/region/locality/scheme — a repeat POST for an identical combination returns 400 DUPLICATE_REGISTRATION.",
        "tags": [
          "Tax Registrations"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RegistrationRequest"
              }
            }
          }
        },
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "201": {
            "description": "Registration recorded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RegistrationResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error — missing country, invalid type, a malformed taxNumber/extraFields for the given scheme, an invalid flat-rate election, an ineligible program enrollment, an ineligible OSS scheme for this entity's own EU establishment, or a duplicate registration for this country/region/locality/scheme (Error.code is one of VALIDATION_ERROR, INVALID_FORMAT, INVALID_IOSS_FORMAT, INVALID_OSS_FORMAT, INVALID_SIMPLIFIED_FORMAT, MISSING_EXTRA_FIELD, UNKNOWN_EXTRA_FIELD, DUPLICATE_REGISTRATION, INVALID_FLAT_RATE_ELECTION, INVALID_PROGRAM_ENROLLMENT, SCHEME_NOT_ELIGIBLE). For DUPLICATE_REGISTRATION, PATCH the existing registration (by `obligationId`) instead of re-POSTing. For INVALID_FLAT_RATE_ELECTION, extraFields.flatRateElection named a country/region/scheme other than a US STANDARD registration in TX/AL, an inactive program, or (TX only) a marketplace-facilitator account. For INVALID_PROGRAM_ENROLLMENT, scheme named a consolidated filing program this state doesn't support enrolling into directly (e.g. Louisiana or Alabama — LA's flat rate is automatic, AL's is recorded as a flat-rate election instead). For SCHEME_NOT_ELIGIBLE, type=UNION_OSS was requested for an entity not established in an EU member state, or type=NON_UNION_OSS was requested for an entity that is (check GET /tax/registrations/field-definitions's `ineligibleSchemes` before submitting to avoid this). Only checked on create, never on an edit to an already-created OSS registration.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A French tax number (country FR, STANDARD scheme) that is already registered to another account. Error code TAX_NUMBER_ALREADY_CLAIMED.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tax/registrations/{id}": {
      "patch": {
        "operationId": "setRegistrationCollection",
        "summary": "Set collection start date, deregister, or edit details",
        "description": "Configure when this entity starts collecting tax for a registered jurisdiction, wind a registration down, set its US flat-rate election, and/or edit its tax number/secondary identifiers. Pass exactly one of `collectFromDate` (start/defer collection — null starts immediately, an ISO date string defers) or `effectiveDate` (deregister — null ends collection immediately, an ISO date string schedules the end). `flatRateElection`, `taxNumber`, and `extraFields` are each independent and additive — send any of them alone, or alongside whichever others you're also setting. Deregistering sets `registrationStatus` to `DEREGISTERED` and records `collectUntilDate`; it keeps the row for history instead of deleting it, and is what a 409 LOAD_BEARING_REGISTRATION from DELETE tells you to use instead. `taxNumber`/`extraFields` never change country/region/locality/scheme — use this to fix a mistyped number or add a secondary identifier (e.g. France's SIRET) instead of deleting and re-adding the registration. Works with an entity-scoped key, or an organisation-scoped key plus the `x-entity-id` header.",
        "tags": [
          "Tax Registrations"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Tax number ID (from the `taxNumberId` field, STANDARD only) or `obligationId` (any scheme)."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "collectFromDate": {
                    "type": "string",
                    "nullable": true,
                    "description": "ISO 8601 date string to defer collection, or null to collect immediately. Mutually exclusive with effectiveDate.",
                    "example": "2025-01-01"
                  },
                  "effectiveDate": {
                    "type": "string",
                    "nullable": true,
                    "description": "ISO 8601 date string to schedule deregistration, or null to deregister immediately. Mutually exclusive with collectFromDate.",
                    "example": "2025-06-30"
                  },
                  "flatRateElection": {
                    "type": "string",
                    "nullable": true,
                    "description": "Canonical encoding (see the `RegistrationRequest.extraFields` example) to set/update this registration's US flat-rate election, or null to clear it. Independent of collectFromDate/effectiveDate — send it alone or combined with either. Only valid on a US STANDARD registration in TX or AL; re-runs the same eligibility check as POST.",
                    "example": "{\"v\":1,\"programCode\":\"TX_SLUTR\",\"electedFrom\":\"2026-01-01\",\"electedTo\":null,\"setBy\":\"ignored\",\"setAt\":\"ignored\"}"
                  },
                  "taxNumber": {
                    "type": "string",
                    "nullable": true,
                    "description": "Edits `registration_number` in place (e.g. fixing a mistyped VAT number) — independent of collectFromDate/effectiveDate/flatRateElection, send it alone or combined with any of them. null or empty string clears it. Re-runs the same country/scheme format check as POST.",
                    "example": "DE123456789"
                  },
                  "extraFields": {
                    "type": "object",
                    "additionalProperties": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "description": "MERGES into the row's existing secondary identifiers (e.g. France's SIRET), per key: a string value overwrites that key, an explicit null deletes it, an omitted key (including an on-file flatRateElection) is left untouched.",
                    "example": {
                      "fr_siret": "12345678901234"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Collection date updated, registration deregistered, and/or the flat-rate election changed — the response merges the fields for whichever body field(s) were sent (e.g. collectFromDate + flatRateElection together yields both collectionStatus/collectFromDate and flatRateElection/flatRateElectionWarning in one response).",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "properties": {
                        "ok": {
                          "type": "boolean"
                        },
                        "collectionStatus": {
                          "type": "string",
                          "enum": [
                            "COLLECTING",
                            "DEFERRED"
                          ]
                        },
                        "collectFromDate": {
                          "type": "string",
                          "format": "date-time",
                          "nullable": true
                        },
                        "flatRateElection": {
                          "$ref": "#/components/schemas/FlatRateElection"
                        },
                        "flatRateElectionWarning": {
                          "$ref": "#/components/schemas/FlatRateElectionWarning"
                        }
                      }
                    },
                    {
                      "type": "object",
                      "properties": {
                        "ok": {
                          "type": "boolean"
                        },
                        "registrationStatus": {
                          "type": "string",
                          "enum": [
                            "DEREGISTERED"
                          ]
                        },
                        "collectUntilDate": {
                          "type": "string",
                          "format": "date-time",
                          "nullable": true
                        },
                        "flatRateElection": {
                          "$ref": "#/components/schemas/FlatRateElection"
                        },
                        "flatRateElectionWarning": {
                          "$ref": "#/components/schemas/FlatRateElectionWarning"
                        }
                      }
                    },
                    {
                      "type": "object",
                      "description": "flatRateElection sent alone, with neither collectFromDate nor effectiveDate.",
                      "properties": {
                        "ok": {
                          "type": "boolean"
                        },
                        "flatRateElection": {
                          "$ref": "#/components/schemas/FlatRateElection"
                        },
                        "flatRateElectionWarning": {
                          "$ref": "#/components/schemas/FlatRateElectionWarning"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "collectFromDate/effectiveDate/flatRateElection missing, not a valid ISO date/string or null, or an ineligible flat-rate election (Error.code is VALIDATION_ERROR, or INVALID_FLAT_RATE_ELECTION when flatRateElection named a country/region/scheme other than a US STANDARD registration in TX/AL, an inactive program, or (TX only) a marketplace-facilitator account).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "404": {
            "description": "Registration not found"
          },
          "409": {
            "description": "A French tax number (country FR, STANDARD scheme) that is already registered to another account. Error code TAX_NUMBER_ALREADY_CLAIMED.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "deleteRegistration",
        "summary": "Delete a tax registration",
        "description": "Remove a tax registration. Works with an entity-scoped key, or an organisation-scoped key plus the `x-entity-id` header. Returns 409 LOAD_BEARING_REGISTRATION for an entity's home-country VAT registration or its default IOSS registration — both are read live by the e-invoicing seller tax ID path; PATCH with `effectiveDate` to deregister instead of deleting.",
        "tags": [
          "Tax Registrations"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Tax number ID (from the `taxNumberId` field, STANDARD only) or `obligationId` (any scheme)."
          }
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Registration deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key"
          },
          "404": {
            "description": "Registration not found"
          },
          "409": {
            "description": "Cannot delete a load-bearing registration — the entity's home-country VAT registration or default IOSS registration (Error.code=LOAD_BEARING_REGISTRATION). PATCH with effectiveDate to deregister instead.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tax/registrations/field-definitions": {
      "get": {
        "operationId": "getRegistrationFieldDefinitions",
        "summary": "Get extra-field requirements for a registration",
        "description": "Look up which `extraFields` keys are required or optional for a given (country, region, scheme) tuple before calling POST /tax/registrations — mirrors the dashboard's \"add registration\" form. For country=US, also returns the full list of valid locality codes so a caller can validate a `locality` value instead of guessing at free text. Read-only; the `fields`/`usLocalityCodes` half of the response depends only on the query params, but `ineligibleSchemes` is entity-scoped (an entity/organisation-scoped key is required for it to be populated). Note: this only covers country/region-scoped fields — a scheme-wide required field like SIMPLIFIED's `voecNumber` (Norway) is not returned here, since it applies regardless of country; POST /tax/registrations still validates and requires it when `type=SIMPLIFIED`.",
        "tags": [
          "Tax Registrations"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "country",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 2,
              "maxLength": 2
            },
            "description": "ISO 3166-1 alpha-2 country code.",
            "example": "US"
          },
          {
            "name": "region",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Sub-national region code, if the field vocabulary is region-specific.",
            "example": "CO"
          },
          {
            "name": "scheme",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "VAT",
                "IOSS",
                "UNION_OSS",
                "NON_UNION_OSS",
                "SIMPLIFIED"
              ]
            },
            "description": "Same `type`-convention values POST /tax/registrations accepts (not the internal STANDARD/OSS_UNION/OSS_NON_UNION spelling). Gates which country-level extra fields appear — e.g. Germany's `steuernummer` only appears for scheme=VAT (STANDARD); omitting this param defaults to VAT's fields, a safe default for an exploratory country selection before a scheme is chosen. Also affects the US flatRateElection field (see its own description).",
            "example": "UNION_OSS"
          }
        ],
        "responses": {
          "200": {
            "description": "Field vocabulary for this country/region",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "country": {
                      "type": "string"
                    },
                    "region": {
                      "type": "string",
                      "nullable": true
                    },
                    "fields": {
                      "type": "array",
                      "description": "Extra-field key definitions applicable to this country/region (e.g. IT regimeFiscale, FR siret, DE steuernummer). Empty for jurisdictions with no known extra-field vocabulary yet — not an error.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "key": {
                            "type": "string",
                            "description": "Key to use in extraFields on POST/PATCH.",
                            "example": "steuernummer"
                          },
                          "label": {
                            "type": "string",
                            "description": "Human-readable label for this field.",
                            "example": "Steuernummer"
                          },
                          "type": {
                            "type": "string",
                            "enum": [
                              "string",
                              "select",
                              "boolean"
                            ]
                          },
                          "required": {
                            "type": "boolean"
                          },
                          "options": {
                            "type": "array",
                            "nullable": true,
                            "description": "Present when type=select.",
                            "items": {
                              "type": "object",
                              "properties": {
                                "value": {
                                  "type": "string"
                                },
                                "label": {
                                  "type": "string"
                                }
                              }
                            }
                          },
                          "placeholder": {
                            "type": "string",
                            "nullable": true
                          },
                          "helpText": {
                            "type": "string",
                            "nullable": true,
                            "description": "For the US flatRateElection field, this is the attestation copy: Clearvo records the election but does not file it with the state or verify eligibility, and the seller must already have filed/enrolled directly with the state."
                          },
                          "btRef": {
                            "type": "string",
                            "nullable": true,
                            "description": "EN16931 Business Term this field corresponds to (e.g. 'BT-32'), where applicable. Never set for US state fields."
                          },
                          "applicableWhen": {
                            "type": "object",
                            "nullable": true,
                            "additionalProperties": true,
                            "description": "invoice-content-field-registry (Germany today) only — the closed predicate that must hold for this field to be relevant to this entity at all (e.g. only once a Handelsregister-registered legal form is declared), in the same machine-readable shape a caller can pre-validate against. Undefined/null means always applicable — every static-manifest/US-state field, and every registry field with no gating condition."
                          },
                          "requiredWhen": {
                            "type": "object",
                            "nullable": true,
                            "additionalProperties": true,
                            "description": "invoice-content-field-registry only — the closed predicate that must hold for this field to become mandatory (evaluated at send/validate time, never at write time — saving a partial profile is never blocked). Undefined/null means the registry never enforces this field as required (enforcement NONE/WARNING on the underlying row)."
                          },
                          "legalBasis": {
                            "type": "string",
                            "nullable": true,
                            "description": "invoice-content-field-registry only — the row's own statute/product-rationale citation (e.g. 'HGB § 37a'), surfaced for the customer, not just an admin screen. Treat as customer-visible copy: the admin write API accepts free text here, so a content-team-authored row can put anything in it."
                          },
                          "requiredHint": {
                            "type": "string",
                            "nullable": true,
                            "description": "invoice-content-field-registry only — human-readable sentence describing when this field becomes required (e.g. \"Required for GmbH, UG, AG, …\"), for the frontend to render instead of a generic \"(optional)\" label. Undefined/null whenever requiredWhen is also undefined/null."
                          },
                          "programMetadata": {
                            "type": "object",
                            "nullable": true,
                            "description": "Present only on the enriched `flatRateElection` field returned for a US STANDARD registration in TX/AL with an active program (scheme=STANDARD, region=TX or AL) — omitted for every other field, and omitted for flatRateElection itself when no program is currently active.",
                            "properties": {
                              "name": {
                                "type": "string",
                                "example": "Single Local Use Tax Rate"
                              },
                              "code": {
                                "type": "string",
                                "example": "TX_SLUTR"
                              },
                              "rate": {
                                "type": "number",
                                "description": "Current rate as a decimal fraction — for TX (LOCAL_COMPONENT_FLAT) this is the local component only, not the combined rate; for AL (TOTAL_FLAT) it's the single rate applied to the whole transaction.",
                                "example": 0.0175
                              },
                              "statute": {
                                "type": "string",
                                "example": "Tex. Tax Code § 151.0595"
                              },
                              "semantics": {
                                "type": "string",
                                "enum": [
                                  "LOCAL_COMPONENT_FLAT",
                                  "TOTAL_FLAT"
                                ]
                              }
                            }
                          }
                        }
                      }
                    },
                    "usLocalityCodes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Valid US home-rule locality codes for this region. Present only when country=US; omitted entirely for every other country.",
                      "example": [
                        "DENVER",
                        "AURORA"
                      ]
                    },
                    "ineligibleSchemes": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "enum": [
                          "UNION_OSS",
                          "NON_UNION_OSS"
                        ]
                      },
                      "description": "Which OSS scheme(s), if any, this entity is NOT eligible to register under, given its own EU establishment — Union OSS requires the filing entity itself to be established in an EU member state; Non-Union OSS requires the reverse (no EU establishment at all), so at most one of the two ever appears here, never both. Present only when the request resolves to a specific entity (an entity-scoped key, or an account-scoped key with X-Entity-Id); omitted entirely otherwise. POST /tax/registrations enforces this same rule server-side — this field only lets a caller hide the ineligible option before submission.",
                      "example": [
                        "NON_UNION_OSS"
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "country query parameter is required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/entities/fact-definitions": {
      "get": {
        "operationId": "getEntityFactDefinitions",
        "summary": "Get entity-level fact definitions",
        "description": "Look up which entityFacts keys exist (e.g. legalForm) before calling PATCH /entities/{id} — mirrors the dashboard's Company Details surface. The fact itself is entity-scoped, not (country, region, scheme)-scoped like GET /tax/registrations/field-definitions — a legal entity has exactly one legal form no matter how many countries it's registered in. Its VALID VALUES are not universal, though: a select-type field's `options` are resolved against the calling entity's own home/establishment country (never an invoice's destination country), since e.g. France's legal forms aren't Germany's. A country with no curated options list yet gets a free-text (`type: \"string\"`) fallback rather than another country's vocabulary.",
        "tags": [
          "Entities"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "Entity fact field vocabulary",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "fields": {
                      "type": "array",
                      "description": "Entity-fact key definitions (e.g. legalForm). Empty if the registry has none defined — not an error.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "key": {
                            "type": "string",
                            "description": "Key to use in entityFacts on PATCH /entities/{id}.",
                            "example": "legalForm"
                          },
                          "label": {
                            "type": "string",
                            "description": "Human-readable label for this field.",
                            "example": "Legal form (Rechtsform)"
                          },
                          "type": {
                            "type": "string",
                            "enum": [
                              "string",
                              "select",
                              "boolean"
                            ]
                          },
                          "required": {
                            "type": "boolean",
                            "description": "Always false — an entity fact is never write-time-required; its actual applicability is conditional, expressed elsewhere (e.g. a registration field's requiredWhen referencing this fact)."
                          },
                          "options": {
                            "type": "array",
                            "nullable": true,
                            "description": "Present when type=select — the closed vocabulary a value must be one of (e.g. legalForm's DE legal-form codes).",
                            "items": {
                              "type": "object",
                              "properties": {
                                "value": {
                                  "type": "string"
                                },
                                "label": {
                                  "type": "string"
                                }
                              }
                            }
                          },
                          "placeholder": {
                            "type": "string",
                            "nullable": true
                          },
                          "helpText": {
                            "type": "string",
                            "nullable": true
                          },
                          "btRef": {
                            "type": "string",
                            "nullable": true
                          },
                          "legalBasis": {
                            "type": "string",
                            "nullable": true,
                            "description": "The statute/product-rationale citation for this fact (e.g. '§37a HGB / §35a GmbHG / §80 AktG / §125a HGB'), customer-visible copy."
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tax/client-codes/options": {
      "get": {
        "operationId": "getClientTaxCodeOptions",
        "summary": "Get valid field options for a client tax code",
        "description": "Returns which movement/rateBand/supplyType values are valid, and whether region/reverseCharge/useTaxSelfAssessed are relevant concepts at all, for a given country/region/direction. This is the single source every client of client tax codes — the dashboard, MCP, CLI, and SDK — should call rather than hardcoding its own valid-options list; the same rules are enforced server-side on POST/PATCH regardless of which client submits. All query params are optional and additive: with none supplied, `movements`/`supplyTypes`/`countries` are returned unfiltered and `rateBands`/the relevance flags are omitted (there's nothing to compute yet); `country` narrows movements/rateBands and resolves the relevance flags; `region` additionally resolves live rateBand percentages; `direction` additionally narrows movements.",
        "tags": [
          "Client Tax Codes"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "country",
            "in": "query",
            "schema": {
              "type": "string",
              "minLength": 2,
              "maxLength": 3
            },
            "example": "GB"
          },
          {
            "name": "region",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Sub-national scope (e.g. a US state) — refines rateBands' resolved rate percentages."
          },
          {
            "name": "direction",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "sale",
                "purchase"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Valid options for the given (or unfiltered, if omitted) country/region/direction — the ClientTaxCodeOptions fields are returned at the top level alongside `ok`, not nested.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "type": "object",
                      "properties": {
                        "ok": {
                          "type": "boolean"
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/ClientTaxCodeOptions"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tax/client-codes/exemption-reasons": {
      "get": {
        "operationId": "getClientTaxCodeExemptionReasons",
        "summary": "Get candidate exemption-reason codes for a client tax code",
        "description": "Derives candidate values for `exemptionReasonCode` (the field POST/PATCH /tax/client-codes accepts) from a Tax Decision's fields — country/movement/taxability/reverseCharge/supplyType/customerType/rateBand — WITHOUT the code needing to exist yet. Lets an integrator (or the dashboard's own create form) populate the exemption-reason picker while a client tax code is still being configured, the same way GET /tax/client-codes/options populates movement/rateBand for an in-progress code. Each candidate carries the format(s) (Peppol, IT FatturaPA, PL KSeF) that would actually emit it; a plain taxable, non-reverse-charged decision (EN16931 category S) never carries an exemption reason, so returns an empty list. Italy's domestic reverse charge (country=IT, movement=local, reverseCharge=true) has nine distinct Natura subtypes (N6.1-N6.9) with no safe single default — every subtype is offered as a candidate for that exact case; every OTHER country's domestic reverse charge instead gets the generic N6.9 (\"altri casi\") fallback alone.",
        "tags": [
          "Client Tax Codes"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "country",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 2,
              "maxLength": 3
            },
            "example": "ES"
          },
          {
            "name": "movement",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "local",
                "intra_community",
                "export",
                "distance_sale",
                "import",
                "own_goods_movement"
              ]
            }
          },
          {
            "name": "taxability",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "taxable",
                "exempt",
                "out_of_scope"
              ]
            }
          },
          {
            "name": "reverseCharge",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "supplyType",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "goods",
                "digital_service",
                "general_service"
              ],
              "default": "goods"
            }
          },
          {
            "name": "customerType",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "b2b",
                "b2c",
                "any"
              ],
              "default": "any"
            }
          },
          {
            "name": "rateBand",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "standard",
                "middle",
                "reduced",
                "super_reduced",
                "special",
                "zero"
              ]
            },
            "description": "Optional — only meaningful for a plain taxable, non-reverse-charged, non-cross-border decision."
          },
          {
            "name": "direction",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "sale",
                "purchase"
              ]
            },
            "description": "Accepted for parity with GET /tax/client-codes/options, but not consumed — an exemption reason means the same thing on a sale or a purchase code."
          }
        ],
        "responses": {
          "200": {
            "description": "Candidate exemption-reason codes for the given Tax Decision fields.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "reasons": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "code": {
                            "type": "string",
                            "description": "The exemption-reason value POST/PATCH /tax/client-codes' exemptionReasonCode field would accept for this decision (a VATEX-* code, an Italian Natura N-code, or a Polish FA(3) rate marker)."
                          },
                          "text": {
                            "type": "string",
                            "description": "Plain-English description of this reason."
                          },
                          "formats": {
                            "type": "array",
                            "items": {
                              "type": "string",
                              "enum": [
                                "PEPPOL",
                                "IT",
                                "PL"
                              ]
                            },
                            "description": "Every output format that would emit this exact code for this decision."
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "ok": true,
                  "reasons": [
                    {
                      "code": "VATEX-EU-79-C",
                      "text": "Exempt from VAT",
                      "formats": [
                        "PEPPOL"
                      ]
                    },
                    {
                      "code": "N4",
                      "text": "Exempt from VAT",
                      "formats": [
                        "IT"
                      ]
                    },
                    {
                      "code": "ZW",
                      "text": "Exempt from VAT",
                      "formats": [
                        "PL"
                      ]
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Missing or unrecognised country/movement/taxability/reverseCharge/supplyType/customerType/rateBand.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tax/codes": {
      "get": {
        "operationId": "listCanonicalTaxCodes",
        "summary": "List/search canonical tax codes",
        "description": "Every tax-code row this platform knows about — Clearvo's own content (`system_enum`: a connector's raw enum value, e.g. Xero TaxType; `fact`: a country's sale-side default treatment, the same vocabulary `lines[].taxTreatment` accepts) plus this caller's own client_tax_codes rows, folded into the same shape (vocabulary=`client`). Use this to build a tax-code picker, or to validate a `clientTaxCode`/`taxTreatment` value before sending it on POST /send. `client` vocabulary rows are always scoped to the caller's own entity — there is no way to list another entity's client tax codes through this endpoint, regardless of query params.",
        "tags": [
          "Client Tax Codes"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "code",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Substring match against `code`, case-insensitive."
          },
          {
            "name": "vocabulary",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "system_enum",
                "fact",
                "client",
                "tax_calc"
              ]
            },
            "description": "Omit to list every vocabulary."
          },
          {
            "name": "country",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "ISO 3166-1 alpha-2 — narrows to this country's rows (system_enum's country-less rows are never excluded by this filter)."
          },
          {
            "name": "sourceSystem",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "e.g. 'xero' — narrows to one connector's system_enum rows; never matches a client row.",
            "example": "xero"
          },
          {
            "name": "date",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Resolves each row's live rate as of this date. Defaults to now."
          }
        ],
        "responses": {
          "200": {
            "description": "Canonical tax-code rows matching the given filters.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "codes": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "vocabulary": {
                            "type": "string",
                            "enum": [
                              "system_enum",
                              "fact",
                              "client",
                              "tax_calc"
                            ]
                          },
                          "country": {
                            "type": "string"
                          },
                          "region": {
                            "type": "string",
                            "nullable": true
                          },
                          "sourceSystem": {
                            "type": "string",
                            "nullable": true
                          },
                          "code": {
                            "type": "string"
                          },
                          "movement": {
                            "type": "string"
                          },
                          "taxability": {
                            "type": "string"
                          },
                          "customerType": {
                            "type": "string",
                            "nullable": true
                          },
                          "supplyType": {
                            "type": "string"
                          },
                          "rateBand": {
                            "type": "string",
                            "nullable": true
                          },
                          "reverseCharge": {
                            "type": "boolean"
                          },
                          "label": {
                            "type": "string",
                            "nullable": true
                          },
                          "description": {
                            "type": "string",
                            "nullable": true
                          },
                          "ratePercent": {
                            "type": "number",
                            "nullable": true,
                            "description": "Live-resolved rate as a percent (23 = 23%) as of `date` — null when this platform can't currently price this row's jurisdiction/date."
                          },
                          "outputPerFormat": {
                            "type": "array",
                            "nullable": true,
                            "description": "This row's EN16931/BIS output category + exemption reason for every supported format (Peppol, IT, PL) — null when ratePercent itself is null.",
                            "items": {
                              "type": "object",
                              "properties": {
                                "format": {
                                  "type": "string",
                                  "enum": [
                                    "PEPPOL",
                                    "IT",
                                    "PL"
                                  ]
                                },
                                "category": {
                                  "type": "string",
                                  "nullable": true
                                },
                                "exemptionReasonCode": {
                                  "type": "string",
                                  "nullable": true
                                },
                                "hold": {
                                  "type": "boolean"
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid vocabulary or date query param.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tax/client-codes": {
      "get": {
        "operationId": "listClientTaxCodes",
        "summary": "List client tax codes",
        "description": "List all client tax codes configured for this entity. A client tax code maps your own ERP tax code (e.g. a SAP two-digit code) to the Clearvo treatment it represents — see POST for the full concept.",
        "tags": [
          "Client Tax Codes"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "Client tax code list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "clientTaxCodes": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ClientTaxCode"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "operationId": "createClientTaxCode",
        "summary": "Create a client tax code",
        "description": "Map one of your own ERP tax codes (e.g. a SAP two-digit code) to a Clearvo Tax Decision — movement, taxability, customerType, supplyType, reverseCharge, useTaxSelfAssessed, and (where meaningful) rateBand. The EN16931 category and `rate` are always computed live from these fields, never stored or caller-supplied. This mapping is consumed two ways: on POST /send, a line item may supply `clientTaxCode` instead of `taxTreatment`/`taxRate` directly, and it is resolved from this table (an INPUT) — an unrecognised code holds the invoice (clearanceStatus HELD_UNMAPPED_TAX_CODE) rather than rejecting it. On POST /tax/calculate, the engine always calculates from the raw transaction data as normal, then looks this table up in reverse — by the fully-resolved Tax Decision, not just country/region/taxCode — and attaches the matching `clientTaxCode` to the response line (an OUTPUT) — it never influences the calculation itself. `code` must be unique per entity — a repeat POST with an existing code returns 409 DUPLICATE_CODE; PATCH the existing one instead. Two different codes mapping to the identical treatment (see DuplicateTreatmentWarning) are NOT rejected — real ERPs sometimes have two internal codes for one identical VAT treatment (e.g. split by GL account) — the response instead carries a non-blocking `warning`.",
        "tags": [
          "Client Tax Codes"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ClientTaxCodeRequest"
              }
            }
          }
        },
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "201": {
            "description": "Client tax code created. `warning` is present only when another code for this entity already maps to the identical treatment — the write still succeeds.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "clientTaxCode": {
                      "$ref": "#/components/schemas/ClientTaxCode"
                    },
                    "warning": {
                      "$ref": "#/components/schemas/DuplicateTreatmentWarning"
                    },
                    "remappedInvoiceCount": {
                      "type": "integer",
                      "description": "How many previously held e-invoices were re-processed after this change. 0 when the API key has no e-invoicing access."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error — missing/invalid code, country, movement, taxability, supplyType, or rateBand (Error.code=VALIDATION_ERROR). rateBand is required whenever taxability=taxable and the movement/reverseCharge combination doesn't already fix the EN16931 code. `taxCode` and `rate` are not request fields — both are always computed from the fields above.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A client tax code with this `code` already exists for this entity (Error.code=DUPLICATE_CODE). PATCH the existing one instead.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tax/client-codes/{id}": {
      "patch": {
        "operationId": "updateClientTaxCode",
        "summary": "Update a client tax code",
        "description": "Update any subset of a client tax code's fields. Same DUPLICATE_CODE (blocking) and DUPLICATE_TREATMENT (non-blocking warning) behaviour as POST.",
        "tags": [
          "Client Tax Codes"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ClientTaxCodeUpdateRequest"
              }
            }
          }
        },
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Client tax code updated. `warning` is present only when another code for this entity already maps to the identical treatment.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "clientTaxCode": {
                      "$ref": "#/components/schemas/ClientTaxCode"
                    },
                    "warning": {
                      "$ref": "#/components/schemas/DuplicateTreatmentWarning"
                    },
                    "remappedInvoiceCount": {
                      "type": "integer",
                      "description": "How many previously held e-invoices were re-processed after this change. 0 when the API key has no e-invoicing access."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error (Error.code=VALIDATION_ERROR)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Client tax code not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "The new `code` collides with a different existing client tax code for this entity (Error.code=DUPLICATE_CODE).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "deleteClientTaxCode",
        "summary": "Delete a client tax code",
        "tags": [
          "Client Tax Codes"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Client tax code deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Client tax code not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tax/client-codes/bulk": {
      "post": {
        "operationId": "bulkImportClientTaxCodes",
        "summary": "Bulk import/upsert client tax codes",
        "description": "Import up to 500 client tax codes in one call, via CSV or JSON. Every row is a FULL definition of the code (not a partial patch) — a matching existing code (same entity + code) is fully overwritten, a new code is created. `code` is the only required column/field; every other field follows the exact same validation as POST/PATCH /tax/client-codes (see ClientTaxCodeRequest), so an invalid movement/rateBand/country combination is rejected identically here, per-row. Rows are processed sequentially (an upsert-by-code lookup must see rows this same batch already wrote), never in parallel.",
        "tags": [
          "Client Tax Codes"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "text/csv": {
              "schema": {
                "type": "string",
                "description": "Header row: code,country,region,direction,movement,taxability,customer_type,supply_type,rate_band,reverse_charge,use_tax_self_assessed,filing_tag,recoverability_type,recoverable_percentage,description"
              }
            },
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "clientTaxCodes"
                ],
                "properties": {
                  "clientTaxCodes": {
                    "type": "array",
                    "maxItems": 500,
                    "items": {
                      "$ref": "#/components/schemas/ClientTaxCodeRequest"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Import processed — `ok` is true only when every row succeeded; check `results`/`summary` for per-row outcomes regardless of the top-level `ok`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "queued": {
                      "type": "integer",
                      "description": "Number of rows submitted."
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "code": {
                            "type": "string"
                          },
                          "id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "taxCode": {
                            "type": "string"
                          },
                          "rate": {
                            "type": "number",
                            "nullable": true
                          },
                          "warning": {
                            "$ref": "#/components/schemas/DuplicateTreatmentWarning"
                          },
                          "error": {
                            "type": "string",
                            "description": "Present only when this row failed."
                          },
                          "errorCode": {
                            "type": "string",
                            "description": "Present only when this row failed."
                          }
                        }
                      }
                    },
                    "summary": {
                      "type": "object",
                      "properties": {
                        "succeeded": {
                          "type": "integer"
                        },
                        "failed": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "No valid rows found, or more than 500 rows submitted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tax/client-codes/audit": {
      "get": {
        "operationId": "getClientTaxCodeAuditLog",
        "summary": "Get a client tax code's audit trail",
        "description": "Read-only history for one client tax code's every field, across every write surface that can touch it (the dashboard, this public API's own create/update/bulk-import routes) — who changed it, when, and old→new values. Queried by `code` rather than by the code's own id, so a deleted code's history stays readable. GET-only by design — an audit trail is evidence, never a resource this API lets a client mutate.",
        "tags": [
          "Client Tax Codes"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "code",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The client tax code to fetch history for."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 25,
              "maximum": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "The `id` of the last entry from a previous page — returns entries strictly older than it (created_at DESC, id DESC keyset ordering). Omit for the first page."
          }
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Audit trail entries, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entries": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "eventType": {
                            "type": "string",
                            "enum": [
                              "client_tax_code.created",
                              "client_tax_code.updated",
                              "client_tax_code.deleted"
                            ]
                          },
                          "actor": {
                            "type": "object",
                            "properties": {
                              "accountId": {
                                "type": "string"
                              },
                              "clerkUserId": {
                                "type": "string",
                                "nullable": true
                              }
                            }
                          },
                          "code": {
                            "type": "string",
                            "nullable": true
                          },
                          "clientTaxCodeId": {
                            "type": "string",
                            "format": "uuid",
                            "nullable": true
                          },
                          "before": {
                            "type": "object",
                            "nullable": true,
                            "additionalProperties": true,
                            "description": "Full pre-change field snapshot. Null only for a create."
                          },
                          "after": {
                            "type": "object",
                            "nullable": true,
                            "additionalProperties": true,
                            "description": "Full post-change field snapshot. Null for a delete."
                          },
                          "duplicateTreatment": {
                            "type": "boolean",
                            "nullable": true
                          },
                          "createdAt": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "hasMore": {
                      "type": "boolean"
                    },
                    "nextCursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing `code`, or an invalid/foreign cursor",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/customer-reference-types": {
      "get": {
        "operationId": "listCustomerReferenceTypes",
        "summary": "List customer reference types",
        "description": "The registry of reference types a customer can carry beyond a tax number: the Peppol participant ID (PEPPOL_PARTICIPANT_ID, `scheme:value`, requires confirmation) and the German Leitweg-ID (LEITWEG_ID, Germany only, one per customer, check digits verified). Use `code` as `type` when writing a customer's `references` (POST /customers, PATCH /customers/{id}, PUT /customers/by-ref/{customerRef}). Platform-wide reference data, sorted for display; any key that can read may call it. Tax and VAT numbers are not references.",
        "tags": [
          "Customers"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "country",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "minLength": 2,
              "maxLength": 2,
              "example": "DE"
            },
            "description": "ISO 3166-1 alpha-2 code. When given, only the types that apply in that country are returned (a type with `countries` null applies everywhere). A malformed value is a 400 INVALID_COUNTRY."
          }
        ],
        "responses": {
          "200": {
            "description": "The applicable reference types.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "referenceTypes"
                  ],
                  "properties": {
                    "referenceTypes": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CustomerReferenceType"
                      }
                    }
                  }
                },
                "example": {
                  "referenceTypes": [
                    {
                      "code": "LEITWEG_ID",
                      "label": "Leitweg-ID",
                      "description": "The routing identifier a German public-sector buyer gives its suppliers so an XRechnung reaches the right department (BT-10).",
                      "helpText": "Routing digits, an optional fine address and two check digits, separated by hyphens. The check digits are verified.",
                      "example": "04011000-1234512345-06",
                      "countries": [
                        "DE"
                      ],
                      "mandates": null,
                      "allowMultiple": false,
                      "requiresConfirmation": false,
                      "easSchemeCode": "0204",
                      "validation": {
                        "kind": "VALIDATOR",
                        "pattern": null
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "`country` is not a two-letter code (INVALID_COUNTRY).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/customers": {
      "get": {
        "operationId": "listCustomers",
        "summary": "List customers",
        "description": "Paginated list of the entity's customer master data.",
        "tags": [
          "Customers"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Case-insensitive substring match against the stored name"
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 25,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "List of customers",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "customers": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Customer"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "page": {
                      "type": "integer"
                    },
                    "limit": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "operationId": "createCustomer",
        "summary": "Create a customer",
        "description": "Create a new customer master-data record. Reference it later via `customer.customerRef` on POST /send instead of resending full customer details every time.",
        "tags": [
          "Customers"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "example": "Acme Retail Ltd"
                  },
                  "country": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 2,
                    "description": "ISO 3166-1 alpha-2 country code of the primary registration. Required together with taxId; omit both (and taxIds) for a B2C customer with no VAT registration. Mutually exclusive with taxIds.",
                    "example": "IE"
                  },
                  "taxId": {
                    "type": "string",
                    "description": "Required together with country. Format-validated and normalized. Mutually exclusive with taxIds.",
                    "example": "IE1234567T"
                  },
                  "taxIds": {
                    "type": "array",
                    "description": "A customer registered in more than one country: the full ordered list of registrations, first entry is the primary (mirrored onto country/taxId in the response). Mutually exclusive with the flat country/taxId fields above.",
                    "items": {
                      "type": "object",
                      "required": [
                        "country",
                        "taxId"
                      ],
                      "properties": {
                        "country": {
                          "type": "string",
                          "minLength": 2,
                          "maxLength": 2,
                          "example": "IE"
                        },
                        "taxId": {
                          "type": "string",
                          "example": "IE1234567T"
                        }
                      }
                    }
                  },
                  "customerRef": {
                    "type": "string",
                    "description": "Your own reference (e.g. CRM/ERP customer id). Must be unique per entity. See also PUT /customers/by-ref/{customerRef} to create-or-update by this reference directly, instead of getting a 409 on a repeat POST.",
                    "example": "CUST-001"
                  },
                  "addressCountry": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 2,
                    "description": "ISO 3166-1 alpha-2 country code of this customer's own mailing address — independent of country/taxIds (the tax registration country). Defaults to the primary tax registration's country when omitted.",
                    "example": "IE"
                  },
                  "addressLine1": {
                    "type": "string"
                  },
                  "addressLine2": {
                    "type": "string"
                  },
                  "city": {
                    "type": "string"
                  },
                  "region": {
                    "type": "string"
                  },
                  "postalCode": {
                    "type": "string"
                  },
                  "references": {
                    "type": "array",
                    "nullable": true,
                    "maxItems": 20,
                    "items": {
                      "$ref": "#/components/schemas/CustomerReferenceInput"
                    },
                    "description": "Typed references for this customer, e.g. [{ \"type\": \"LEITWEG_ID\", \"value\": \"04011000-1234512345-06\" }] or [{ \"type\": \"PEPPOL_PARTICIPANT_ID\", \"value\": \"0204:04011000-1234512345-06\" }]. See GET /v1/customer-reference-types for the types. A Peppol participant ID supplied here is treated as confirmed immediately. Each item is validated; a failure is a 422 INVALID_REFERENCES listing every bad item. The former `peppolParticipantId` field has been removed."
                  },
                  "countrySpecific": {
                    "type": "object",
                    "description": "Country-specific master data — only meaningful for the named country.",
                    "properties": {
                      "it": {
                        "type": "object",
                        "properties": {
                          "codiceDestinatario": {
                            "type": "string",
                            "minLength": 7,
                            "maxLength": 7,
                            "description": "This customer's 7-character SDI routing channel code.",
                            "example": "ABC1234"
                          },
                          "pecDestinatario": {
                            "type": "string",
                            "format": "email",
                            "description": "This customer's PEC (certified email) delivery address."
                          }
                        }
                      },
                      "ar": {
                        "type": "object",
                        "properties": {
                          "condicionIVAReceptorId": {
                            "type": "integer",
                            "enum": [
                              1,
                              4,
                              5,
                              6,
                              7,
                              8,
                              9,
                              10,
                              13,
                              15,
                              16
                            ],
                            "description": "Argentina AFIP/ARCA buyer VAT condition — ARCA's real FEParamGetCondicionIvaReceptor code (RG (ARCA) 5616/2024): 1=Responsable Inscripto, 4=IVA Sujeto Exento, 5=Consumidor Final, 6=Responsable Monotributo, 7=Sujeto No Categorizado, 8=Proveedor del Exterior, 9=Cliente del Exterior, 10=IVA Liberado (Ley 19.640), 13=Monotributista Social, 15=IVA No Alcanzado, 16=Monotributo Trabajador Independiente Promovido. Codes 2 and 3 are obsolete and no longer valid."
                          }
                        }
                      },
                      "de": {
                        "type": "object",
                        "properties": {
                          "invoiceFormat": {
                            "type": "string",
                            "enum": [
                              "ZUGFERD",
                              "XRECHNUNG",
                              "PEPPOL"
                            ],
                            "description": "Per-customer default German invoice format, applied by POST /send whenever the request omits countrySpecific.de.invoiceFormat."
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "201": {
            "description": "Customer created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "customer": {
                      "$ref": "#/components/schemas/Customer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error — e.g. taxId provided without country",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A customer with this customerRef already exists for this entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Either the provided tax ID failed format validation for the given country (the standard Error shape), or one or more `references` items failed validation (INVALID_REFERENCES, see CustomerReferencesInvalidResponse); in the second case nothing was written and error.references lists every failing item.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "$ref": "#/components/schemas/CustomerReferencesInvalidResponse"
                    }
                  ]
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/customers/{id}": {
      "get": {
        "operationId": "getCustomer",
        "summary": "Get a customer",
        "tags": [
          "Customers"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Customer record",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Customer not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "operationId": "updateCustomer",
        "summary": "Update a customer",
        "description": "Update any subset of fields. country and taxId are treated as a pair — setting one to null without the other clears taxId (the pair is no longer complete). Mutually exclusive with taxIds.",
        "tags": [
          "Customers"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "country": {
                    "type": "string",
                    "nullable": true,
                    "minLength": 2,
                    "maxLength": 2
                  },
                  "taxId": {
                    "type": "string",
                    "nullable": true
                  },
                  "taxIds": {
                    "type": "array",
                    "description": "Full replacement list of this customer's tax registrations (pass [] to clear every one); first entry becomes the primary, mirrored onto country/taxId in the response. Mutually exclusive with the flat country/taxId fields. Omit this field entirely to leave existing registrations untouched.",
                    "items": {
                      "type": "object",
                      "required": [
                        "country",
                        "taxId"
                      ],
                      "properties": {
                        "country": {
                          "type": "string",
                          "minLength": 2,
                          "maxLength": 2,
                          "example": "IE"
                        },
                        "taxId": {
                          "type": "string",
                          "example": "IE1234567T"
                        }
                      }
                    }
                  },
                  "customerRef": {
                    "type": "string",
                    "nullable": true
                  },
                  "addressCountry": {
                    "type": "string",
                    "nullable": true,
                    "minLength": 2,
                    "maxLength": 2,
                    "description": "ISO 3166-1 alpha-2 country code of this customer's own mailing address — independent of country/taxIds. Omit to leave unchanged; null clears it.",
                    "example": "IE"
                  },
                  "addressLine1": {
                    "type": "string",
                    "nullable": true
                  },
                  "addressLine2": {
                    "type": "string",
                    "nullable": true
                  },
                  "city": {
                    "type": "string",
                    "nullable": true
                  },
                  "region": {
                    "type": "string",
                    "nullable": true
                  },
                  "postalCode": {
                    "type": "string",
                    "nullable": true
                  },
                  "references": {
                    "type": "array",
                    "nullable": true,
                    "maxItems": 20,
                    "items": {
                      "$ref": "#/components/schemas/CustomerReferenceInput"
                    },
                    "description": "Replaces the customer's whole list of typed references when present. Omit to leave the stored references untouched; null or [] clears them all. A Peppol participant ID supplied here is treated as confirmed immediately (re-sending the stored value keeps its confirmation and reachability). See GET /v1/customer-reference-types."
                  },
                  "countrySpecific": {
                    "type": "object",
                    "description": "Country-specific master data — only meaningful for the named country.",
                    "properties": {
                      "it": {
                        "type": "object",
                        "properties": {
                          "codiceDestinatario": {
                            "type": "string",
                            "nullable": true,
                            "minLength": 7,
                            "maxLength": 7,
                            "description": "This customer's 7-character SDI routing channel code. Null clears it.",
                            "example": "ABC1234"
                          },
                          "pecDestinatario": {
                            "type": "string",
                            "format": "email",
                            "nullable": true,
                            "description": "This customer's PEC (certified email) delivery address. Null clears it."
                          }
                        }
                      },
                      "de": {
                        "type": "object",
                        "properties": {
                          "invoiceFormat": {
                            "type": "string",
                            "nullable": true,
                            "enum": [
                              "ZUGFERD",
                              "XRECHNUNG",
                              "PEPPOL"
                            ],
                            "description": "Per-customer default DE invoice format, applied by POST /send whenever the request itself omits countrySpecific.de.invoiceFormat for this customer (most-specific-wins: request > this customer default > entity default > platform default ZUGFERD). Null clears the override, falling through to the entity/platform default."
                          }
                        }
                      },
                      "ar": {
                        "type": "object",
                        "properties": {
                          "condicionIVAReceptorId": {
                            "type": "integer",
                            "nullable": true,
                            "enum": [
                              1,
                              4,
                              5,
                              6,
                              7,
                              8,
                              9,
                              10,
                              13,
                              15,
                              16
                            ],
                            "description": "Argentina AFIP/ARCA buyer VAT condition, applied by POST /send whenever the request itself omits countrySpecific.ar.condicionIVAReceptorId for this customer. See the create-customer condicionIVAReceptorId description for the full ARCA code list. Null clears the stored value."
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Customer updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Customer not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A customer with this customerRef already exists for this entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Either the provided tax ID failed format validation for the given country (the standard Error shape), or one or more `references` items failed validation (INVALID_REFERENCES, see CustomerReferencesInvalidResponse); in the second case nothing was written and error.references lists every failing item.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "$ref": "#/components/schemas/CustomerReferencesInvalidResponse"
                    }
                  ]
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "deleteCustomer",
        "summary": "Delete a customer (soft delete)",
        "description": "Soft-delete a customer. If this customer is invoiced again, they will be re-added automatically.",
        "tags": [
          "Customers"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "204": {
            "description": "Customer deleted"
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Customer not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/customers/by-ref/{customerRef}": {
      "put": {
        "operationId": "upsertCustomerByRef",
        "summary": "Create or update a customer by your reference",
        "description": "Create-or-update in one call, keyed on customerRef instead of Clearvo's internal id — the natural operation for syncing customer master data from your own CRM/ERP, where 'push the current state of this customer' is run repeatedly, not a one-time create. Unlike POST /customers (which 409s on a repeat customerRef), this always succeeds: creates the row on first call, REPLACES it on every later call with the same customerRef. Your system of record is treated as authoritative — an omitted optional field clears whatever was previously stored, exactly like a standard REST PUT. The exceptions are `references` and countrySpecific.it.codiceDestinatario/pecDestinatario: omitted, each is left untouched (not cleared), so a plain CRM/ERP field sync doesn't need to also resend references (a Peppol participant ID, a Leitweg-ID) or Italy routing data set separately (e.g. via the dashboard) just to avoid wiping it. When `references` is present it replaces the whole list; null or [] clears it.",
        "tags": [
          "Customers"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "customerRef",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your own reference for this customer (e.g. CRM/ERP customer id)."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "example": "Acme Retail Ltd"
                  },
                  "country": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 2,
                    "description": "ISO 3166-1 alpha-2 country code of the primary registration. Required together with taxId; omit both (and taxIds) for a B2C customer with no VAT registration. Mutually exclusive with taxIds.",
                    "example": "IE"
                  },
                  "taxId": {
                    "type": "string",
                    "description": "Required together with country. Mutually exclusive with taxIds.",
                    "example": "IE1234567T"
                  },
                  "taxIds": {
                    "type": "array",
                    "description": "Full list of this customer's tax registrations, first entry is the primary. Mutually exclusive with the flat country/taxId fields above.",
                    "items": {
                      "type": "object",
                      "required": [
                        "country",
                        "taxId"
                      ],
                      "properties": {
                        "country": {
                          "type": "string",
                          "minLength": 2,
                          "maxLength": 2,
                          "example": "IE"
                        },
                        "taxId": {
                          "type": "string",
                          "example": "IE1234567T"
                        }
                      }
                    }
                  },
                  "addressCountry": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 2,
                    "description": "ISO 3166-1 alpha-2 country code of this customer's own mailing address — independent of country/taxIds. Defaults to the primary tax registration's country when omitted; same full-replace semantics as every other field here (an omission on a later call clears back to that default, it does not preserve a prior explicit value).",
                    "example": "IE"
                  },
                  "addressLine1": {
                    "type": "string"
                  },
                  "addressLine2": {
                    "type": "string"
                  },
                  "city": {
                    "type": "string"
                  },
                  "region": {
                    "type": "string"
                  },
                  "postalCode": {
                    "type": "string"
                  },
                  "references": {
                    "type": "array",
                    "nullable": true,
                    "maxItems": 20,
                    "items": {
                      "$ref": "#/components/schemas/CustomerReferenceInput"
                    },
                    "description": "Replaces the customer's whole list of typed references when present; null or [] clears them all. Omit to leave the stored references untouched (like countrySpecific.it, they are not cleared by a plain CRM/ERP field sync). A Peppol participant ID supplied here is treated as confirmed immediately. See GET /v1/customer-reference-types."
                  },
                  "countrySpecific": {
                    "type": "object",
                    "description": "Country-specific master data — only meaningful for the named country. Omit a sub-field to leave it untouched (same posture as `references`), not clear it.",
                    "properties": {
                      "it": {
                        "type": "object",
                        "properties": {
                          "codiceDestinatario": {
                            "type": "string",
                            "minLength": 7,
                            "maxLength": 7,
                            "description": "This customer's 7-character SDI routing channel code.",
                            "example": "ABC1234"
                          },
                          "pecDestinatario": {
                            "type": "string",
                            "format": "email",
                            "description": "This customer's PEC (certified email) delivery address."
                          }
                        }
                      },
                      "ar": {
                        "type": "object",
                        "properties": {
                          "condicionIVAReceptorId": {
                            "type": "integer",
                            "enum": [
                              1,
                              4,
                              5,
                              6,
                              7,
                              8,
                              9,
                              10,
                              13,
                              15,
                              16
                            ],
                            "description": "Argentina AFIP/ARCA buyer VAT condition. See the create-customer condicionIVAReceptorId description for the full ARCA code list. Omitted, leaves any existing stored value untouched."
                          }
                        }
                      },
                      "de": {
                        "type": "object",
                        "properties": {
                          "invoiceFormat": {
                            "type": "string",
                            "enum": [
                              "ZUGFERD",
                              "XRECHNUNG",
                              "PEPPOL"
                            ],
                            "description": "Per-customer default German invoice format, applied by POST /send whenever the request omits countrySpecific.de.invoiceFormat."
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Customer created or updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "customer": {
                      "$ref": "#/components/schemas/Customer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error — e.g. taxId provided without country, or an empty customerRef in the URL",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Either the provided tax ID failed format validation for the given country (the standard Error shape), or one or more `references` items failed validation (INVALID_REFERENCES, see CustomerReferencesInvalidResponse); in the second case nothing was written and error.references lists every failing item.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "$ref": "#/components/schemas/CustomerReferencesInvalidResponse"
                    }
                  ]
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/suppliers": {
      "get": {
        "operationId": "listSuppliers",
        "summary": "List suppliers",
        "description": "Paginated list of the entity's supplier master data.",
        "tags": [
          "Suppliers"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Case-insensitive substring match against the stored name"
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 25,
              "maximum": 100
            }
          },
          {
            "name": "trusted",
            "in": "query",
            "schema": {
              "type": "boolean"
            },
            "description": "Pass true to return only trusted suppliers (those with the AP-decision full override set — see PATCH /suppliers/{id}). Any other value returns all suppliers."
          }
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "List of suppliers",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "suppliers": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Supplier"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "page": {
                      "type": "integer"
                    },
                    "limit": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "operationId": "createSupplier",
        "summary": "Create a supplier",
        "description": "Create a new supplier master-data record. Reference it later via `supplier.ref` on POST /tax/calculate (transactionDirection: 'purchase') instead of resending full supplier details every time.",
        "tags": [
          "Suppliers"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "example": "Acme Supplies Ltd"
                  },
                  "country": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 2,
                    "description": "ISO 3166-1 alpha-2 country code of the primary registration. Required together with taxId; omit both (and taxIds) for a supplier with no known VAT registration. Mutually exclusive with taxIds.",
                    "example": "IE"
                  },
                  "taxId": {
                    "type": "string",
                    "description": "Required together with country. Format-validated and normalized. Mutually exclusive with taxIds.",
                    "example": "IE1234567T"
                  },
                  "taxIds": {
                    "type": "array",
                    "description": "A supplier registered in more than one country: the full ordered list of registrations, first entry is the primary (mirrored onto country/taxId in the response). Mutually exclusive with the flat country/taxId fields above.",
                    "items": {
                      "type": "object",
                      "required": [
                        "country",
                        "taxId"
                      ],
                      "properties": {
                        "country": {
                          "type": "string",
                          "minLength": 2,
                          "maxLength": 2,
                          "example": "IE"
                        },
                        "taxId": {
                          "type": "string",
                          "example": "IE1234567T"
                        }
                      }
                    }
                  },
                  "supplierRef": {
                    "type": "string",
                    "description": "Your own reference (e.g. ERP vendor id). Must be unique per entity.",
                    "example": "VEND-001"
                  },
                  "establishmentCountry": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 2,
                    "description": "ISO 3166-1 alpha-2 country code of this supplier's own place of establishment — independent of country/taxIds (the tax registration country). Defaults to the primary tax registration's country when omitted.",
                    "example": "IE"
                  },
                  "addressLine1": {
                    "type": "string"
                  },
                  "addressLine2": {
                    "type": "string"
                  },
                  "city": {
                    "type": "string"
                  },
                  "region": {
                    "type": "string"
                  },
                  "postalCode": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "201": {
            "description": "Supplier created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "supplier": {
                      "$ref": "#/components/schemas/Supplier"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error — e.g. taxId provided without country",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A supplier with this supplierRef already exists for this entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The provided tax ID failed format validation for the given country",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/suppliers/{id}": {
      "get": {
        "operationId": "getSupplier",
        "summary": "Get a supplier",
        "tags": [
          "Suppliers"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Supplier record",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Supplier"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Supplier not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "operationId": "updateSupplier",
        "summary": "Update a supplier",
        "description": "Update any subset of fields. country and taxId are treated as a pair — setting one to null without the other clears taxId (the pair is no longer complete). Mutually exclusive with taxIds.",
        "tags": [
          "Suppliers"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "country": {
                    "type": "string",
                    "nullable": true,
                    "minLength": 2,
                    "maxLength": 2
                  },
                  "taxId": {
                    "type": "string",
                    "nullable": true
                  },
                  "taxIds": {
                    "type": "array",
                    "description": "Full replacement list of this supplier's tax registrations (pass [] to clear every one); first entry becomes the primary, mirrored onto country/taxId in the response. Mutually exclusive with the flat country/taxId fields. Omit this field entirely to leave existing registrations untouched.",
                    "items": {
                      "type": "object",
                      "required": [
                        "country",
                        "taxId"
                      ],
                      "properties": {
                        "country": {
                          "type": "string",
                          "minLength": 2,
                          "maxLength": 2,
                          "example": "IE"
                        },
                        "taxId": {
                          "type": "string",
                          "example": "IE1234567T"
                        }
                      }
                    }
                  },
                  "supplierRef": {
                    "type": "string",
                    "nullable": true
                  },
                  "establishmentCountry": {
                    "type": "string",
                    "nullable": true,
                    "minLength": 2,
                    "maxLength": 2,
                    "description": "ISO 3166-1 alpha-2 country code of this supplier's own place of establishment — independent of country/taxIds. Omit to leave unchanged; null clears it.",
                    "example": "IE"
                  },
                  "addressLine1": {
                    "type": "string",
                    "nullable": true
                  },
                  "addressLine2": {
                    "type": "string",
                    "nullable": true
                  },
                  "city": {
                    "type": "string",
                    "nullable": true
                  },
                  "region": {
                    "type": "string",
                    "nullable": true
                  },
                  "postalCode": {
                    "type": "string",
                    "nullable": true
                  },
                  "trusted": {
                    "type": "boolean",
                    "description": "AP decision layer full override — every vendor-charged-tax verification for this supplier then resolves ACCEPTED_AS_CHARGED regardless of delta. Enterprise only (Purchases (AP) sub-solution): setting it without Purchases (AP) is refused with 403 `ap_not_included` or `ap_not_enabled`; clearing is always allowed."
                  },
                  "toleranceOverride": {
                    "type": "object",
                    "nullable": true,
                    "description": "null clears it (falls through to the entity default again). Enterprise only (Purchases (AP) sub-solution): setting it without Purchases (AP) is refused with 403 `ap_not_included` or `ap_not_enabled`; clearing is always allowed.",
                    "properties": {
                      "absoluteAmount": {
                        "type": "number",
                        "minimum": 0
                      },
                      "percent": {
                        "type": "number",
                        "minimum": 0
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Supplier updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Supplier"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Supplier not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A supplier with this supplierRef already exists for this entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The provided tax ID failed format validation for the given country",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "deleteSupplier",
        "summary": "Delete a supplier (soft delete)",
        "description": "Soft-delete a supplier. If a received e-invoice references this supplier again, they will be re-added automatically.",
        "tags": [
          "Suppliers"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "204": {
            "description": "Supplier deleted"
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Supplier not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/suppliers/by-ref/{ref}": {
      "get": {
        "operationId": "getSupplierByRef",
        "summary": "Get a supplier by your reference",
        "description": "Look up a supplier by supplierRef instead of Clearvo's internal id — the natural lookup when you only have your own ERP vendor id on hand.",
        "tags": [
          "Suppliers"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "ref",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your own reference for this supplier (e.g. ERP vendor id)."
          }
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Supplier record",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Supplier"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Supplier not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/company-codes": {
      "get": {
        "operationId": "listCompanyCodes",
        "summary": "List company codes",
        "description": "The entity's company codes, ordered by code. A company code is an internal split of one entity's transactions (an SAP company code, a Coupa business unit): the same taxable person and registrations, a separate bucket for rules and Explore. Enterprise only.",
        "tags": [
          "Company Codes"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "OAuthBearer": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Pass active to leave archived codes out.",
            "schema": {
              "type": "string",
              "enum": [
                "active"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The entity's company codes.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "companyCodes"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "companyCodes": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CompanyCode"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Company codes are included in the Enterprise plan (error company_codes_not_included).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error",
                    "message",
                    "upgradeUrl"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "company_codes_not_included"
                      ]
                    },
                    "message": {
                      "type": "string"
                    },
                    "upgradeUrl": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createCompanyCode",
        "summary": "Add a company code",
        "description": "Codes are compared without regard to case or surrounding spaces and never change once created. Optional validFrom/validTo (YYYY-MM-DD, both inclusive, null = no limit) restrict the transaction dates the code may be used on; validTo must not be before validFrom. Enterprise only. Set the entity default with PATCH /tax/settings { defaultCompanyCode }.",
        "tags": [
          "Company Codes"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "OAuthBearer": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "code"
                ],
                "properties": {
                  "code": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 50,
                    "description": "The code as your ERP writes it, trimmed."
                  },
                  "name": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 200
                  },
                  "validFrom": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "date",
                    "description": "Optional first day (YYYY-MM-DD, inclusive) the code is valid. Omitted or null: no start limit (there is no default start date)."
                  },
                  "validTo": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "date",
                    "description": "Optional last day (YYYY-MM-DD, inclusive) the code is valid. Omitted or null: no end limit. Must not be before validFrom."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "companyCode"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "companyCode": {
                      "$ref": "#/components/schemas/CompanyCode"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Company codes are included in the Enterprise plan (error company_codes_not_included).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error",
                    "message",
                    "upgradeUrl"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "company_codes_not_included"
                      ]
                    },
                    "message": {
                      "type": "string"
                    },
                    "upgradeUrl": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "The entity already has this code (company_code_exists; the existing row is returned as existing)."
          },
          "422": {
            "description": "Validation failed."
          }
        }
      }
    },
    "/company-codes/{id}": {
      "patch": {
        "operationId": "updateCompanyCode",
        "summary": "Rename, archive or reactivate a company code",
        "description": "There is no DELETE and code cannot be changed. validFrom/validTo can be set, changed or cleared (null). Archiving the entity's default company code is refused (409 company_code_is_default); change the default in tax settings first. Enterprise only.",
        "tags": [
          "Company Codes"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "OAuthBearer": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "minProperties": 1,
                "properties": {
                  "name": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 200
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "active",
                      "archived"
                    ]
                  },
                  "validFrom": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "date",
                    "description": "First day (YYYY-MM-DD, inclusive) the code is valid. null clears the start limit; omit to leave unchanged."
                  },
                  "validTo": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "date",
                    "description": "Last day (YYYY-MM-DD, inclusive) the code is valid. null clears the end limit; omit to leave unchanged. Must not be before validFrom."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "companyCode"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "companyCode": {
                      "$ref": "#/components/schemas/CompanyCode"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Company codes are included in the Enterprise plan (error company_codes_not_included).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error",
                    "message",
                    "upgradeUrl"
                  ],
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "company_codes_not_included"
                      ]
                    },
                    "message": {
                      "type": "string"
                    },
                    "upgradeUrl": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No company code with that id on this entity (company_code_not_found)."
          },
          "409": {
            "description": "The code is the entity's default company code (company_code_is_default)."
          },
          "422": {
            "description": "Validation failed."
          }
        }
      }
    },
    "/bank-accounts": {
      "get": {
        "operationId": "listBankAccounts",
        "summary": "List bank accounts",
        "description": "List the entity's stored bank accounts (IBAN/BIC master data).",
        "tags": [
          "Bank Accounts"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "List of bank accounts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "bankAccounts": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/BankAccount"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "operationId": "createBankAccount",
        "summary": "Store a bank account",
        "description": "Store a bank account for this entity. Omit currency for the entity-wide DEFAULT account (used when no currency-specific account matches an invoice). At most one account per currency, and at most one DEFAULT, per entity.",
        "tags": [
          "Bank Accounts"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "iban"
                ],
                "properties": {
                  "currency": {
                    "type": "string",
                    "minLength": 3,
                    "maxLength": 3,
                    "description": "ISO 4217 alpha-3 currency code. Omit for the entity-wide DEFAULT account.",
                    "example": "EUR"
                  },
                  "iban": {
                    "type": "string",
                    "description": "Format/checksum-validated IBAN.",
                    "example": "IE29AIBK93115212345678"
                  },
                  "bic": {
                    "type": "string",
                    "description": "BIC/SWIFT code.",
                    "example": "AIBKIE2D"
                  },
                  "accountHolderName": {
                    "type": "string",
                    "description": "Dashboard display only — never rendered onto an invoice."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "201": {
            "description": "Bank account created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "bankAccount": {
                      "$ref": "#/components/schemas/BankAccount"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A bank account for this currency (or the DEFAULT slot) already exists for this entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The provided IBAN failed format/checksum validation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/bank-accounts/{id}": {
      "get": {
        "operationId": "getBankAccount",
        "summary": "Get a bank account",
        "tags": [
          "Bank Accounts"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Bank account record",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BankAccount"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Bank account not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "operationId": "updateBankAccount",
        "summary": "Update a bank account",
        "description": "Update any subset of fields. currency: null sets it back to the entity-wide DEFAULT slot.",
        "tags": [
          "Bank Accounts"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "currency": {
                    "type": "string",
                    "nullable": true,
                    "minLength": 3,
                    "maxLength": 3
                  },
                  "iban": {
                    "type": "string",
                    "description": "Re-validated on change."
                  },
                  "bic": {
                    "type": "string",
                    "nullable": true
                  },
                  "accountHolderName": {
                    "type": "string",
                    "nullable": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Bank account updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BankAccount"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Bank account not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A bank account for this currency (or the DEFAULT slot) already exists for this entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The provided IBAN failed format/checksum validation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "deleteBankAccount",
        "summary": "Delete a bank account (soft delete)",
        "description": "Soft-delete a stored bank account.",
        "tags": [
          "Bank Accounts"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "204": {
            "description": "Bank account deleted"
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Bank account not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tax/exemptions": {
      "get": {
        "operationId": "listExemptionCertificates",
        "summary": "List exemption certificates",
        "description": "Paginated list of exemption certificates for the entity.",
        "tags": [
          "Exemption Certificates"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "ACTIVE",
                "EXPIRING_SOON",
                "EXPIRED",
                "REVOKED"
              ]
            },
            "description": "Filter by the certificate's stored status (case-insensitive). EXPIRING_SOON and EXPIRED are written by the renewal job; the response's expiryStatus is always recomputed live from effectiveTo."
          },
          {
            "name": "country",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Filter by ISO country code"
          },
          {
            "name": "region",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Filter by region/state code"
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 25,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "List of exemption certificates",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "certificates": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Certificate"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "page": {
                      "type": "integer"
                    },
                    "limit": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "operationId": "createExemptionCertificate",
        "summary": "Create an exemption certificate",
        "description": "Create a new exemption certificate record for a customer. Once created, reference it via `customer.exemptionRef` on tax calculation requests to apply the exemption to eligible line items.\n\n`certificateType`/`formType` are validated together against `country` — only certain combinations are supported per country, and an unsupported combination (e.g. a US certificate type for a country with no such concept, or a missing/wrong `formType` for a scheme that requires one) is rejected with 400, not silently accepted. Supported combinations today:\n\n| country | certificateType | formType |\n|---|---|---|\n| `US` | `RESALE`, `MANUFACTURING`, `AGRICULTURAL`, `ENERGY`, `EXEMPT_ORG`, `GOVERNMENT`, `DIRECT_PAY`, `BLANKET_OTHER` | optional — `SST`, `MTC`, `CUSTOM`, or a state-specific form code |\n| `US` | `DIGITAL_SERVICES_COMMERCIAL_USE` | required — must be `31-014A`; also requires `region: \"IA\"` and `taxCategorySlug` of `saas_business` or `saas_personal` |\n| `IE` | `EXPORT_AUTHORIZATION` | required — must be `56B` |\n\n`DIGITAL_SERVICES_COMMERCIAL_USE`/`31-014A` represents Iowa's exemption for SaaS/digital services sold to a commercial enterprise for exclusive commercial use (Iowa Code §423.3(104)) — Form 31-014A's digital-services checkbox, on Iowa's general-purpose exemption form (blanket or single-purchase, customer's choice). Unlike most US certificate types, this one is legally scoped to one specific state and two specific product categories, not a blanket exemption from everything — `region` and `taxCategorySlug` are required, not optional, for this type.\n\n`IE`/`EXPORT_AUTHORIZATION`/`56B` represents Irish Revenue's Section 56 authorisation: a habitual exporter's authorisation letting Irish suppliers zero-rate sales to them. This is a Revenue-issued, customer-held authorisation checked by the supplier before relying on it — not the same mechanism as EU cross-border reverse charge (which is VAT-number based) or a US self-asserted exemption certificate. It does not cover food/drink, accommodation, entertainment, or personal-services categories even when a valid authorisation is on file — apply judgement on those line items regardless of an active certificate. More non-US schemes (e.g. Italy's plafond IVA, France's achat en franchise de TVA) are researched but not yet supported — creating a certificate for any other country currently returns 400.",
        "tags": [
          "Exemption Certificates"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "certificateRef",
                  "customerRef",
                  "certificateType",
                  "effectiveFrom"
                ],
                "properties": {
                  "certificateRef": {
                    "type": "string",
                    "description": "The number printed on the certificate (for a US resale certificate, the buyer's state-issued permit or account number). Checked against the state's documented number format, warn-only: a number that does not match is still saved, with `certificate.numberFormatStatus` set to MISMATCH and a `CERTIFICATE_NUMBER_FORMAT` entry in `warnings`. Formats are currently held for Texas, Florida, Wisconsin, Pennsylvania and Illinois resale certificates; other states are not checked.",
                    "example": "EXEMPT-2024-001"
                  },
                  "customerRef": {
                    "type": "string",
                    "description": "Your internal customer reference. Used to look up this certificate automatically when `customer.ref` matches during tax calculation.",
                    "example": "CUST-001"
                  },
                  "customerName": {
                    "type": "string",
                    "description": "Exempt customer's name."
                  },
                  "customerTaxId": {
                    "type": "string",
                    "description": "Exempt customer's tax ID."
                  },
                  "certificateType": {
                    "type": "string",
                    "description": "Valid values depend on `country` — see the table above.",
                    "enum": [
                      "RESALE",
                      "MANUFACTURING",
                      "AGRICULTURAL",
                      "ENERGY",
                      "EXEMPT_ORG",
                      "GOVERNMENT",
                      "DIRECT_PAY",
                      "BLANKET_OTHER",
                      "DIGITAL_SERVICES_COMMERCIAL_USE",
                      "EXPORT_AUTHORIZATION"
                    ]
                  },
                  "formType": {
                    "type": "string",
                    "description": "Standard form type. Optional for most US types; required and must be `31-014A` for `certificateType: DIGITAL_SERVICES_COMMERCIAL_USE`; required and must be `56B` for `country: IE`.",
                    "example": "SST"
                  },
                  "country": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 2,
                    "default": "US",
                    "description": "ISO 3166-1 alpha-2 country code. Only countries in the supported-combinations table above are accepted.",
                    "example": "US"
                  },
                  "region": {
                    "type": "string",
                    "description": "State or region code the exemption applies to. Required and must be `IA` for `certificateType: DIGITAL_SERVICES_COMMERCIAL_USE`. Not applicable for IE — Section 56 authorisations are national, not sub-regional.",
                    "example": "CA"
                  },
                  "taxCategorySlug": {
                    "type": "string",
                    "description": "Restricts the exemption to a specific product tax category. Optional for most certificate types (omit to cover all categories); required and must be `saas_business` or `saas_personal` for `certificateType: DIGITAL_SERVICES_COMMERCIAL_USE`."
                  },
                  "effectiveFrom": {
                    "type": "string",
                    "format": "date",
                    "description": "Date from which the certificate is valid.",
                    "example": "2024-01-01"
                  },
                  "effectiveTo": {
                    "type": "string",
                    "format": "date",
                    "description": "Expiry date of the certificate. Omit for open-ended certificates. For IE 56B, use the expiry date printed on the Revenue authorisation.",
                    "example": "2024-12-31"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "201": {
            "description": "Certificate created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "certificate": {
                      "$ref": "#/components/schemas/Certificate"
                    },
                    "warnings": {
                      "type": "array",
                      "description": "Non-blocking notices about the certificate that was saved. Empty when there is nothing to flag.",
                      "items": {
                        "type": "object",
                        "required": [
                          "code",
                          "message"
                        ],
                        "properties": {
                          "code": {
                            "type": "string",
                            "enum": [
                              "CERTIFICATE_NUMBER_FORMAT"
                            ],
                            "description": "CERTIFICATE_NUMBER_FORMAT: `certificateRef` does not match the state's documented number format."
                          },
                          "message": {
                            "type": "string",
                            "description": "Plain-English text to show the user."
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error, or an unsupported country/certificateType/formType combination (see the supported-combinations table above).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Certificate with this reference already exists",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tax/exemptions/{id}": {
      "get": {
        "operationId": "getExemptionCertificate",
        "summary": "Get an exemption certificate",
        "tags": [
          "Exemption Certificates"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Certificate record",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Certificate"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Certificate not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "operationId": "updateExemptionCertificate",
        "summary": "Update an exemption certificate",
        "description": "Update the status, expiry date, or document URL of an existing certificate.",
        "tags": [
          "Exemption Certificates"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "REVOKED"
                    ],
                    "description": "Set to REVOKED to invalidate the certificate immediately."
                  },
                  "effectiveTo": {
                    "type": "string",
                    "format": "date",
                    "description": "Update or set the expiry date.",
                    "example": "2025-12-31"
                  },
                  "documentUrl": {
                    "type": "string",
                    "format": "uri",
                    "description": "URL of the uploaded certificate document."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Certificate updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Certificate"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Certificate not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "deleteExemptionCertificate",
        "summary": "Delete an exemption certificate (soft delete)",
        "description": "Soft-delete a certificate. The record is retained for audit purposes but will no longer match new transactions.",
        "tags": [
          "Exemption Certificates"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Certificate deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Certificate not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tax/calculate": {
      "post": {
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "operationId": "calculateTax",
        "summary": "Calculate tax",
        "description": "Synchronous single-transaction tax calculation. Resolves the jurisdiction (including US special-territory postal codes), classifies line items, looks up applicable rates, and returns the full result. Set commit=true to persist the calculation to the tax_calculations ledger and count it toward compliance threshold monitoring. Degrades gracefully — never returns a 500 on calculation errors; falls back to rate=0 with degraded=true instead.",
        "tags": [
          "Tax Calculations"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "OAuthBearer": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TaxCalculateRequest"
              }
            }
          }
        },
        "responses": {
          "403": {
            "description": "Either this API key is not authorized for the specified entity (entity-scoped key against a different entity, or an organisation-scoped key's allowlist does not include the entity named in x-entity-id), or the request is purchase-side (transactionDirection `purchase` or documentStage `purchase_order`) and Purchases (AP) is not available: `ap_not_included` (Enterprise plan required) or `ap_not_enabled` (switch it on under Settings > Solutions).",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    { "$ref": "#/components/schemas/Error" },
                    { "$ref": "#/components/schemas/ApNotAvailable" }
                  ]
                }
              }
            }
          },
          "200": {
            "description": "Successful calculation. A well-formed country we do not calculate tax for (for example JE, GG, IM or QA) is NOT an error: it answers 200 with totalTax 0, taxTreatment UNSUPPORTED_JURISDICTION (taxCode O), a top-level unsupportedJurisdiction field ({ country, party }) and a country_not_supported entry in warnings. Each affected line carries outcome { status: 'UNKNOWN_TREATMENT', reasonCode: 'unsupported_jurisdiction' }. The transaction is stored (when commit is true) and shown in the dashboard as an unsupported country. GET /v1/tax/jurisdictions lists the supported countries.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaxCalculateResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid JSON body, or a `customProperties` key (header or line) that is not allowed. `UNKNOWN_CUSTOM_PROPERTY`: the key is not defined (define it with POST /rules-engine/properties; the error carries a `fixUrl`). `BUILT_IN_PROPERTY_AS_CUSTOM`: the key names a built-in field such as `glAccount`, `costCenter`, `intendedUse`, `accountAssignment`, `commodityCode` or `commodityCodeScheme`. Body: `{\"error\":\"built_in_property_as_custom\",\"code\":\"BUILT_IN_PROPERTY_AS_CUSTOM\",\"message\":\"... send it as lineItems[1].glAccount\",\"propertyKey\":\"glAccount\"}`, where `message` names the flat field to send instead.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A previous request with the same idempotencyKey (commit=true) is still being processed — error idempotency_key_in_progress. Retry shortly to receive its result rather than treating this as a validation failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request body too large — the JSON body must be under 512KB (524288 bytes). Body: {\"error\":\"Request body too large\",\"hint\":\"Body must be under 524288 bytes.\"}.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation error (e.g. missing US state, credit_note without relatedCalculationId), or a country that is not a valid ISO 3166-1 alpha-2 code (for example \"XX\", \"Narnia\" or \"UK\") — error invalid_country with code INVALID_COUNTRY, `field` naming the offending request path (for example customer.shippingAddress.country), `value` (what was sent), `message` and `hint`. A valid country we do not support is NOT a 422: it returns 200 with taxTreatment UNSUPPORTED_JURISDICTION (see the 200 response and GET /tax/jurisdictions). Also returns error us_tax_treatment_override_unsupported if a line's taxTreatmentOverride names STANDARD/MIDDLE/REDUCED/SUPER_REDUCED/SPECIAL but the line resolves to a US jurisdiction, where those bands have no meaning — see the TaxTreatmentOverride schema. Also returns error category_not_supported when one or more line items classify into a tax category this engine has no place-of-supply sourcing logic for yet (every offending line named in `lines`), and error vat_id_dispatch_country_mismatch when a GOODS line fails the EU VAT Directive Art. 138(1)(b) intra-community check — the customer's VAT-ID country equals the goods' dispatch country, with a different ship-to country (every offending line named in `lines`). The `supplier`/`customer` counterparty contract is checked before any other work. On transactionDirection='sale' (the default): code=CUSTOMER_REQUIRED when `customer` (the counterparty) is omitted; `supplier` is optional and auto-enriched from your entity's own master data — if you do supply it, code=SUPPLIER_TAX_ID_MISMATCH (`field: \"supplier\"`) when it isn't one of the entity's own registered tax IDs (any country — see POST /tax/registrations; omit it to auto-fill from master data). On transactionDirection='purchase': code=SUPPLIER_REQUIRED when `supplier` (the counterparty) is omitted; `customer` is optional and auto-enriched the same way — code=CUSTOMER_TAX_ID_MISMATCH (`field: \"customer\"`) on the same registered-tax-ID mismatch. A `supplier.ref` that does not resolve to a saved supplier of this entity returns code=SUPPLIER_REF_NOT_FOUND (`field: \"supplier\"`) — create it with POST /v1/suppliers, or send the vendor's taxId/billingAddress directly. `seller` (the deprecated alias for `supplier`) is never valid on a purchase — code=SELLER_ALIAS_NOT_ALLOWED_FOR_PURCHASE (`field: \"seller\"`), since there is no sensible mapping. Every one of these codes (CUSTOMER_REQUIRED, SUPPLIER_REQUIRED, SELLER_ALIAS_NOT_ALLOWED_FOR_PURCHASE, SUPPLIER_TAX_ID_MISMATCH, CUSTOMER_TAX_ID_MISMATCH, SUPPLIER_REF_NOT_FOUND) returns the same shape: `error`, `code`, `field` (the field to fix), `message`, and `hint`. For transactionDirection='purchase' specifically: returns error us_jurisdiction_purchase_unsupported (a US-resolved jurisdiction — no use-tax self-assessment computation exists yet; rejects the whole transaction) or supplier_country_required_for_purchase (one or more GOODS lines' real supplier country — supplier.billingAddress.country / supplier.shippingAddress.country in purchase mode — could not be determined at all; an unknown supplier country is never treated as domestic, and `lines` names every offending line), each with `code` and `country`. A genuinely cross-border GOODS purchase whose supplier country CAN be determined is no longer rejected here — it is computed per line instead, and a line this engine can't fully resolve is surfaced as a non-blocking lineItems[].outcome: { status: 'UNKNOWN_TREATMENT', reasonCode } on the 200 response (see the LineOutcomeReasonCode schema) rather than any 422. Returns a plain field-validation error if the request supplies `clientTaxCode` (top-level or line-level) at all — it is not accepted as input on this endpoint; send it on POST /v1/send instead. Also returns error company_code_not_valid_on_date when the named companyCode is not valid on the transaction's own invoice/tax-point date (outside the code's validFrom/validTo window); the entity default code outside its window is not applied instead of rejected.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "$ref": "#/components/schemas/InvalidCountryError"
                    }
                  ]
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "402": {
            "description": "Free-tier plan: the transaction was recorded for Compliance Radar but the plan does not include tax calculation results — error plan_required, with monitorOnly: true, the calculationId of the recorded row, a message and an upgradeUrl to the billing settings. (A WooCommerce checkout request instead receives a 200 with fallbackToNativeTax.)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "plan_required"
                      ]
                    },
                    "calculationId": {
                      "type": "string"
                    },
                    "monitorOnly": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "message": {
                      "type": "string"
                    },
                    "upgradeUrl": {
                      "type": "string",
                      "format": "uri"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "A credit note's original calculation could not be read to inherit its marketplace-facilitator flag (error credit_note_original_unavailable) — the credit note was not calculated. Retry, or send isMarketplaceFacilitatedSale explicitly. A Retry-After: 5 header is set.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "listTaxCalculations",
        "summary": "List tax calculations",
        "description": "Paginated list of committed tax calculations for one entity: an entity-scoped key's own entity, or the entity an organisation-scoped key names in x-entity-id (or the entityId query parameter). Only committed calculations (commit=true) appear here. A single call never spans more than one entity.",
        "tags": [
          "Tax Calculations"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "entityId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Narrow to one entity the key is authorized for. Defaults to the entity named in x-entity-id; an organisation-scoped key never lists across entities in one call."
          },
          {
            "name": "country",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Filter by jurisdiction country (ISO 3166-1 alpha-2)."
          },
          {
            "name": "documentStage",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": ["invoice", "purchase_order"]
            },
            "description": "Filter by document stage: invoice or purchase_order. Omit for both. Any other value returns 400. `purchase_order` is Enterprise only (Purchases (AP)): 403 `ap_not_included` or `ap_not_enabled` otherwise."
          },
          {
            "name": "direction",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": ["sale", "purchase"]
            },
            "description": "Filter by transaction direction. Omit for both. Any other value returns 400. `purchase` is Enterprise only (Purchases (AP)): 403 `ap_not_included` or `ap_not_enabled` otherwise."
          },
          {
            "name": "purchaseOrderId",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Exact purchase-order reference. Returns the purchase-order calculation with that id plus every calculation whose lines reference it."
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 25,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "List of tax calculations",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "calculations": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TaxCalculationListItem"
                      }
                    },
                    "pagination": {
                      "type": "object",
                      "properties": {
                        "page": {
                          "type": "integer"
                        },
                        "limit": {
                          "type": "integer"
                        },
                        "total": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/duties/quote": {
      "post": {
        "operationId": "quoteDuties",
        "summary": "Quote import duties",
        "description": "Estimated import duty, import VAT/GST and customs fees for a cross-border cart, without recording a calculation. The body is the POST /tax/calculate body: `duties.include` is implied (an explicit false is 422), and `commit`, credit notes (`transactionType: credit_note`, `relatedCalculationId`, `externalOriginalReference`) and `transactionDirection: purchase` are rejected with 422. Stateless: nothing is stored and no idempotency key is read. Everything returned is an estimate; none of it is tax and none of it changes a tax figure. A duty failure is a 200 with `duties.status: degraded`, never a 5xx; the one non-2xx the engine can add is the fail-closed 503 `tax_content_unavailable`. Send `commodityCode` (with `commodityCodeScheme`), `countryOfOrigin` and `weight` per line to tighten the estimate, and `incoterms` or `importerOfRecord` to say who imports. A `partner_calc` key calls this path too, with the same required `sellerRegistrationContext` and `partnerMerchantId` as POST /tax/calculate/partner.\n\nShow the buyer by `responsibleParty` and `collectedAtCheckout`: seller-responsible consignments need nothing extra; a buyer-responsible consignment is an optional delivery notice, or, with `duties.collectDeposit: true`, `summary.importChargesAtCheckout` as its own line labelled \"Estimated import charges\" with `summary.totalAmountDue` as the amount to charge.\n\nAttribution: UK tariff data: contains public sector information licensed under the Open Government Licence v3.0. EU Combined Nomenclature: source acknowledged per Commission Decision 2011/833/EU. US Harmonized Tariff Schedule: public domain.",
        "tags": [
          "Tax Calculations"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "OAuthBearer": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TaxCalculateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Duty quote",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DutiesResult"
                }
              }
            }
          },
          "400": {
            "description": "Invalid JSON body",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request body too large."
          },
          "422": {
            "description": "Invalid body, or a field this endpoint does not accept (commit, credit note fields, purchase direction, duties.include false).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "description": "tax_content_unavailable: tax content is temporarily unavailable, so no tax basis exists. Retry after the Retry-After interval.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/duties/estimate": {
      "post": {
        "operationId": "estimateDuties",
        "summary": "Estimate duties for one product across destinations",
        "description": "Estimated import duty, import VAT/GST, customs fees and landed cost for ONE product shipped to up to 20 destinations: a pricing call for setting delivered-duty-paid prices per market. Same engine, content and estimate/precision semantics as POST /duties/quote: each destination is the one-line cart a quote caller would send. Nothing is recorded: no calculation, no id, no idempotency key, no catalogue write, so a read-only key is enough. Each destination comes back with a `status` (quoted, degraded or not_applicable) and the quote's `duties`, `consignments` and `summary` blocks plus the product line's `duty`; one bad destination (for example a US destination without a region) is a degraded entry and never fails the call. The top-level `product` block carries the resolved commodity code, scheme, `codeSource` and precision. Everything is an estimate and none of it is tax. The one non-2xx the engine can add is the fail-closed 503 `tax_content_unavailable`. A `partner_calc` key calls this path too, with `supplier`, `sellerRegistrationContext` and `partnerMerchantId` in the body.",
        "tags": [
          "Tax Calculations"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "OAuthBearer": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DutiesEstimateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Estimate per destination",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DutiesEstimateResult"
                }
              }
            }
          },
          "400": {
            "description": "Invalid JSON body",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request body too large."
          },
          "422": {
            "description": "Invalid body: unknown field, no product identifier, both or neither of `amount` and `unitPrice`, more than 20 or repeated destinations, or a partner-only field on an entity key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "description": "tax_content_unavailable: tax content is temporarily unavailable. Retry after the Retry-After interval.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/tax/calculate/{id}/import-settlement": {
      "post": {
        "operationId": "recordImportSettlement",
        "summary": "Record what customs actually charged",
        "description": "After clearance, post the customs entry number and the actual duty, import VAT/GST and fees from the entry or the carrier's duty-and-tax invoice for a committed calculation that carries a quoted duties block. The settlement is stored append-only next to a snapshot of the estimate, with the variance computed server-side (actual minus estimated, per component, as an amount and a percent) and a `withinEstimate` flag. Idempotent on calculation, consignment and `entryNumber`: replaying the same actuals returns the stored settlement (200, `replayed: true`); different actuals for the same entry are 409, and a correction is a new `entryNumber`. `actual.currency` must equal the calculation's currency: no conversion is performed (422 `currency_mismatch`). Stores facts only: it never changes the calculation, its tax figures or the entity's tax obligations. Entity-scoped keys only.",
        "tags": [
          "Tax Calculations"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "OAuthBearer": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Calculation ID (cl_calc_ prefix)."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ImportSettlementRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Settlement recorded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ImportSettlementResult"
                }
              }
            }
          },
          "200": {
            "description": "The same entry was already recorded with the same actuals; the stored settlement",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ImportSettlementResult"
                }
              }
            }
          },
          "400": {
            "description": "Invalid JSON body",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Calculation not found for this entity, or `consignmentId` is not one of its consignments.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`settlement_conflict`: this entry is already settled for the consignment with different actuals. The stored settlement is returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request body too large."
          },
          "422": {
            "description": "Invalid body, or: `currency_mismatch`, `calculation_is_credit_note`, `calculation_not_committed`, `calculation_not_a_sale`, `no_duties_block`, `no_quoted_consignment`, `consignment_required` (several consignments, send `consignmentId`), `consignment_not_quoted`, `line_not_found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "get": {
        "operationId": "getImportSettlements",
        "summary": "List a calculation's import settlements",
        "description": "The settlements recorded against a calculation, each with estimated against actual and the variance, and a summary per currency (estimated, actual, variance, within-estimate count).",
        "tags": [
          "Tax Calculations"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "OAuthBearer": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Calculation ID (cl_calc_ prefix)."
          }
        ],
        "responses": {
          "200": {
            "description": "Settlements and summary",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ImportSettlementList"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Calculation not found for this entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/duties/settlement-variance": {
      "get": {
        "operationId": "getImportSettlementVariance",
        "summary": "Estimate-versus-entry variance by destination and precision",
        "description": "Aggregate of the entity's import settlements: count, mean and median of the total variance percent, and amounts per currency, grouped by destination customs territory and the precision tier the estimate was quoted at. Filters are optional. Read-only and entity-scoped.",
        "tags": [
          "Tax Calculations"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "OAuthBearer": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Earliest entry date (the day the settlement was recorded when it carries none)."
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Latest entry date."
          },
          {
            "name": "destination",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Destination customs territory id, for example `EU`, `GB`, `US`."
          },
          {
            "name": "precision",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Precision tier at quote time, for example `TARIFF_LINE`."
          }
        ],
        "responses": {
          "200": {
            "description": "Variance groups",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ImportSettlementVariance"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "A filter is malformed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tax/calculation-ledger": {
      "get": {
        "operationId": "listCalculationRequests",
        "summary": "List calculation requests (ledger)",
        "description": "The calculation request ledger for one entity: one row per tax-calculation request made with the entity's keys, INCLUDING commit=false quotes and requests that were rejected (4xx/422, with a short error code). Rows hold facts and hashes only, never the request or response body: endpoint, committed flag, currency, origin and destination, total amount and tax, a hash of the tax-relevant inputs, a hash of the tax-relevant result, duration and HTTP outcome. A committed calculation also carries a `quote` object linking it to the last matching commit=false quote of the same key (same inputs, else the same merchantRef) within the quote window, with `differsFromQuote` (the tax-relevant result differs), `inputsChanged`, and the tax and amount deltas. Rows are kept for 13 months. Ledger writes are asynchronous and eventually consistent: a request appears within about 30 seconds, and a replica that crashes without a graceful shutdown can lose up to roughly 10 seconds of rows. Pass `summary` to get per-group request counts instead of rows. A well-formed country we do not support is recorded with outcome `unsupported_country` (status 200, error code country_not_supported, destination country = the unsupported country); filter with ?outcome=unsupported_country and group with ?summary=country to see demand by destination country.",
        "tags": [
          "Tax Calculations"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "committed",
            "in": "query",
            "schema": {
              "type": "boolean"
            },
            "description": "true = committed calculations only; false = commit=false quotes only."
          },
          {
            "name": "differsFromQuote",
            "in": "query",
            "schema": {
              "type": "boolean"
            },
            "description": "true = committed calculations whose tax-relevant result differs from the linked quote."
          },
          {
            "name": "outcome",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "ok",
                "degraded",
                "rejected",
                "error",
                "replayed",
                "unsupported_country"
              ]
            }
          },
          {
            "name": "minStatus",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 100,
              "maximum": 599
            },
            "description": "Only rows with an HTTP status at or above this value, e.g. 400 for rejected requests."
          },
          {
            "name": "endpoint",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "tax_calculate",
                "tax_calculate_partner",
                "csv_import",
                "integration",
                "dashboard"
              ]
            }
          },
          {
            "name": "keyId",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Filter by API key id."
          },
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Inclusive start."
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Exclusive end."
          },
          {
            "name": "summary",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "key",
                "entity",
                "country"
              ]
            },
            "description": "Return per-group request counts (requests, committed, quotes, rejected, degraded, differsFromQuote, distinctSources) instead of rows."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 200
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "nextCursor from the previous response."
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Ledger rows (or groups when summary is set)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "rows": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CalculationLedgerRow"
                      }
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "groups": {
                      "type": "array",
                      "description": "Present instead of rows when summary is set.",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "A filter value is malformed (for example limit, from, to, cursor).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tax/calculate/import": {
      "post": {
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "operationId": "importTaxCalculations",
        "summary": "Bulk import transactions from CSV",
        "description": "Upload a CSV of historical (backdated) or current transactions to be calculated and recorded as real tax calculations — a dedicated, calc-only ingestion path (no document generation, no authority submission). Every import is final: there is no preview or confirm step. Always asynchronous: this call only lands the file to storage and returns a batchId; a background job then calculates each transaction with commit:true, committing clean transactions immediately. A transaction that errors is NOT committed and is reported (see GET /tax/calculate/import/{batchId}/errors); the batch never fails as a whole for a partially-clean file. Poll GET /tax/calculate/import/{batchId} for progress and each transaction's outcome. One or more adjacent CSV rows sharing the same transactionRef column become one multi-line transaction (each row is a line item) — rows for the same transactionRef must be consecutive in the file, or the whole upload is rejected with a structural error. Required CSV columns: transactionRef, transactionDate, currency, customerCountry, productName (customerRegion is additionally required whenever customerCountry is US or CA). Full column list: transactionRef, transactionDate, currency, customerCountry, customerRegion, customerPostalCode, customerTaxId, customerType, customerRef, shippingCountry, shippingRegion, shippingPostalCode, productName, productCode, amount (or unitPrice+quantity), amountIncludesTax, shippingCarrier, chargeAvoidable, actualCostOfShipment, transactionType, externalOriginalReference, shipFromCountry, vatValidation. The three shipping columns are optional and apply to US shipping_handling lines: shippingCarrier is common or seller_vehicle; chargeAvoidable and actualCostOfShipment are true or false (case-insensitive); leave a cell empty to omit the fact, which an account default shipping rule may then fill. Any other value rejects that transaction with a row-numbered error. Currency/country/US-state values are auto-normalized (e.g. \"US dollar\" -> USD, \"Germany\" -> DE) via the same normalisation every /v1/tax/calculate request goes through.",
        "tags": [
          "Tax Calculations"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "OAuthBearer": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "The CSV file. Must be at most 200MB."
                  },
                  "filename": {
                    "type": "string",
                    "description": "Optional — overrides the display filename (defaults to the uploaded file's own name)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "201": {
            "description": "File accepted — processing has been queued.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "batchId": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "UPLOADED"
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "No file provided, or the file is not a .csv",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "411": {
            "description": "Content-Length is required — chunked transfer encoding is not supported on this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Upload too large — the CSV body must be at most 200MB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "description": "Bulk import is not configured for this environment, the file could not be stored, or it could not be queued for processing — retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "listTaxCalculationImports",
        "summary": "List CSV import batches",
        "description": "Paginated list of this entity's CSV bulk import batches, newest first.",
        "tags": [
          "Tax Calculations"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 25,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "List of import batches",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "batches": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TaxCalcImportBatch"
                      }
                    },
                    "pagination": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "integer"
                        },
                        "page": {
                          "type": "integer"
                        },
                        "limit": {
                          "type": "integer"
                        },
                        "pages": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tax/calculate/import/{batchId}": {
      "get": {
        "operationId": "getTaxCalculationImport",
        "summary": "Get an import batch's status and transactions",
        "description": "Batch status plus a paginated page of its transaction groups — each carries its final outcome: COMMITTED (with its calculationId) or ERROR (nothing committed, with a reason). total, committedCount and errorCount advance while the batch is PROCESSING.",
        "tags": [
          "Tax Calculations"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "batchId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 25,
              "maximum": 100
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "COMMITTED",
                "ERROR"
              ]
            },
            "description": "Filters the transactions page only, by outcome — not the batch itself."
          }
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Batch status and transactions",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "batch": {
                      "$ref": "#/components/schemas/TaxCalcImportBatch"
                    },
                    "transactions": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TaxCalcImportTransaction"
                      }
                    },
                    "pagination": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "integer"
                        },
                        "page": {
                          "type": "integer"
                        },
                        "limit": {
                          "type": "integer"
                        },
                        "pages": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Batch not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "cancelTaxCalculationImport",
        "summary": "Cancel an import batch",
        "description": "Cancels a batch that has not started processing. Only valid from UPLOADED — once processing starts, transactions may already be committed.",
        "tags": [
          "Tax Calculations"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "batchId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Cancelled",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "batchId": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "CANCELLED"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Batch not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Batch cannot be cancelled from its current status (not UPLOADED).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tax/calculate/import/{batchId}/errors": {
      "get": {
        "operationId": "listTaxCalculationImportErrors",
        "summary": "Download an import batch's errored transactions",
        "description": "Every transaction group of a batch that was NOT committed, as a downloadable CSV (row range, transaction ref, error code, and message) so a customer can fix their source file and re-upload just the affected transactions. Pass ?format=json to get the same rows as JSON instead of a file download.",
        "tags": [
          "Tax Calculations"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "batchId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "json"
              ]
            },
            "description": "Omit for a CSV file download (the default)."
          }
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "CSV download (default) or JSON (?format=json)",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string",
                  "description": "Header row: transactionRef,rowStart,rowEnd,errorCode,errorMessage"
                }
              },
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "errors": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "transactionRef": {
                            "type": "string"
                          },
                          "rowStart": {
                            "type": "integer"
                          },
                          "rowEnd": {
                            "type": "integer"
                          },
                          "errorCode": {
                            "type": "string",
                            "nullable": true
                          },
                          "errorMessage": {
                            "type": "string",
                            "nullable": true
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Batch not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tax/calculate/{id}/refund": {
      "post": {
        "operationId": "refundTaxCalculation",
        "summary": "Mark a calculation as refunded, fully or partially",
        "description": "Mark a committed tax calculation as refunded — fully, or partially via an optional request body. Used by B2C billing systems (Stripe, Chargebee, etc.) when a payment is refunded and no separate credit note document is issued. The refunded amount is excluded from compliance threshold totals shortly after this call returns. For B2B credit notes from ERP systems, use transactionType=credit_note on a new POST /v1/tax/calculate instead. Only entity-scoped keys are accepted. Sandbox calculations cannot be refunded. A calculation can be refunded across multiple calls (partial refunds) as long as each call supplies a distinct idempotencyKey and the cumulative refunded amount never exceeds the calculation's total.",
        "tags": [
          "Tax Calculations"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "OAuthBearer": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Calculation ID (cl_calc_ prefix)."
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "amount": {
                    "type": "number",
                    "description": "Amount being refunded by this event. Defaults to the calculation's remaining (unrefunded) balance — i.e. a full refund of whatever's left — when omitted."
                  },
                  "idempotencyKey": {
                    "type": "string",
                    "maxLength": 100,
                    "description": "Dedupes this specific refund event. Required whenever amount is less than the remaining balance (a genuine partial refund) — a second partial refund omitting this would otherwise collide with the first and be silently dropped. Optional for a full refund, where it defaults to the calculation id itself."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Refund applied",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    },
                    "calculationId": {
                      "type": "string"
                    },
                    "amount": {
                      "type": "number",
                      "description": "The amount refunded by this call."
                    },
                    "remaining": {
                      "type": "number",
                      "description": "The remaining refundable balance after this call."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Organisation-scoped key used (entity-scoped required), invalid JSON body, invalid amount, or invalid/missing idempotencyKey for a partial refund",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Calculation not found or does not belong to this entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Calculation already fully refunded, or this exact refund event (idempotencyKey) was already applied",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Sandbox calculation, credit note (cannot be refunded via this endpoint), or amount exceeds the remaining refundable balance",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tax/obligations": {
      "get": {
        "operationId": "listObligations",
        "summary": "List tax obligations",
        "description": "List tax obligations for one entity: an entity-scoped key's own entity, or the entity an organisation-scoped key names in x-entity-id. Each obligation row represents a jurisdiction where the entity has (or is monitoring) a tax registration. Includes threshold data, current-period revenue, and collection status. A single call never spans more than one entity. On an Enterprise account whose administrator has not switched Compliance Radar on, answers 403 `solution_not_enabled` with `solution: compliance_radar` (no upgradeUrl; the administrator decides).",
        "tags": [
          "Tax Calculations"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "OAuthBearer": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "country",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Filter by ISO country code."
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Filter by obligation_status (e.g. COMPLIANT, APPROACHING, BREACH)."
          }
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "List of obligations",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "obligations": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Obligation"
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tax/obligations/{id}": {
      "patch": {
        "operationId": "updateObligation",
        "summary": "Update a tax obligation",
        "description": "Update the registration status, registration number, obligation status, or detail summary for a specific tax obligation. The obligation must belong to the caller's entity or account. At least one field is required. When Compliance Radar is switched off for the account (Enterprise), the response echoes `obligationStatus`, `thresholdAmount`, `thresholdCurrency` and `currentPeriodAmount` as null and adds `obligationStatusLabel` \"Not monitored\"; the write is applied and stored values are untouched.",
        "tags": [
          "Tax Calculations"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Obligation ID."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "registrationStatus": {
                    "type": "string",
                    "maxLength": 50,
                    "example": "REGISTERED"
                  },
                  "registrationNumber": {
                    "type": "string",
                    "maxLength": 120,
                    "example": "US-CA-1234567"
                  },
                  "obligationStatus": {
                    "type": "string",
                    "maxLength": 50,
                    "example": "COMPLIANT"
                  },
                  "detailSummary": {
                    "type": "string",
                    "maxLength": 2000,
                    "description": "Free-text notes about this obligation."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Updated obligation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Obligation"
                }
              }
            }
          },
          "400": {
            "description": "Missing required fields or invalid JSON",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Obligation not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/reporting-batches": {
      "get": {
        "operationId": "listReportingBatches",
        "summary": "List reporting batches (Spain SII, France e-reporting, and any other batching regime)",
        "description": "List the reporting batches this key can see, newest first — the groups of transactions a batching regime (Spain SII's `batch_auto`/`batch_review` submissionMode, or France e-reporting's décade accumulation) gathers before submitting them together (POST /send returns 200 ACCUMULATED with the `batchId` for each record that joins one). Each row carries the batch's status, submission mode, record count, its own report-by deadline, and a server-computed `overdueLabel` once that deadline has passed. Use `status=ready_for_review` to find batches waiting for your confirm, `status=open` to see what is still accumulating, or the pseudo-values `overdue`/`needs_attention` to find batches needing action. Omit `obligation`/`mandate` to see every batching regime's rows, or pass one to filter to it. Every response includes a `vocabulary` block with labels for every status and record state — render from it rather than hardcoding.",
        "tags": [
          "Reporting Batches"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "One or more comma-separated ReportingPeriodStatus values (open, closed, ready_for_review, submitted, filed) and/or the pseudo-values `overdue` (report-by deadline has passed with no terminal outcome yet) or `needs_attention` (the batch carries an attention_reason — can match a submitted/filed row too, unlike overdue). Any other value is rejected with 400."
          },
          {
            "name": "obligation",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "A regime code (case-insensitive — e.g. ES_SII, fr_ereporting). Omit to include every batching regime. `mandate` is accepted as an alias for this same parameter."
          },
          {
            "name": "mandate",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Alias for `obligation` — same meaning, same values."
          },
          {
            "name": "entityId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Filter by entity (organisation-scoped keys only; ignored for an entity-scoped key). Must be a UUID — 400 otherwise; 403 if the key's allowlist does not include it."
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Every reporting batch this key can see, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "batches",
                    "pagination",
                    "vocabulary"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    },
                    "batches": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ReportingBatch"
                      }
                    },
                    "pagination": {
                      "type": "object",
                      "properties": {
                        "total": {
                          "type": "integer"
                        },
                        "page": {
                          "type": "integer"
                        },
                        "limit": {
                          "type": "integer"
                        },
                        "pages": {
                          "type": "integer"
                        },
                        "hasNext": {
                          "type": "boolean"
                        },
                        "hasPrev": {
                          "type": "boolean"
                        }
                      }
                    },
                    "vocabulary": {
                      "$ref": "#/components/schemas/ReportingBatchVocabulary"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid `status` value, a malformed `entityId` (must be a UUID), or an account-scoped key with no x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id or entityId.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Failed to query reporting batches",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/reporting-batches/{id}": {
      "get": {
        "operationId": "getReportingBatch",
        "summary": "Get one Spain SII reporting batch with its records",
        "description": "Full detail for one batch: everything the list row carries plus `snapshotVersion` (the concurrency token POST .../confirm must echo), `snapshotHash`, `reviewedBy`/`reviewedAt`, and `records[]` — every registro in the batch with its invoice number, counterparty NIF, tipo, book, base/cuota, SII classification, ingestion warnings, its own report-by date and `isLate`, the AEAT outcome once submitted (PENDING before), and — for a record that was excluded from this batch — aeatOutcome.estado `EXCLUDED` with `excludedBy` (reason, actor, when, and the batch it moved to). Poll this after a confirm to watch `status` move submitted → filed and each record's `aeatOutcome` fill in.",
        "tags": [
          "Reporting Batches"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The batch's `id` (UUID) from GET /reporting-batches — not its human-readable `batchId`."
          }
        ],
        "responses": {
          "200": {
            "description": "Full batch detail.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "batch",
                    "vocabulary"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    },
                    "batch": {
                      "$ref": "#/components/schemas/ReportingBatchDetail"
                    },
                    "vocabulary": {
                      "$ref": "#/components/schemas/ReportingBatchVocabulary"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed `id` (must be a UUID), or an account-scoped key with no x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such batch, or it belongs to an entity this key is not scoped to (cross-entity access reads identically to not-found — never a distinct 403).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Failed to fetch reporting batch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/reporting-batches/{id}/confirm": {
      "post": {
        "operationId": "confirmReportingBatch",
        "summary": "Confirm a reporting batch for submission",
        "description": "Approve a batch so it is submitted to the authority now: the human step a `batch_review` obligation exists for (France e-reporting defaults to `batch_review`: Clearvo never submits such a period on its own, even past the deadline, it only reminds you by email and webhook, and you remain the liable party; a `batch_auto` batch, Spain SII or France e-reporting, is confirmed by the sweep, but this endpoint also works on it to submit early). Confirm works once the batch has `closed` (its period has ended) or is frozen `ready_for_review`. A batch whose period is still `open` is refused 409 BATCH_STILL_OPEN: GET /reporting-batches/{id} works at any time, POST .../confirm becomes valid once the period closes. On success the batch is handed to the regime's submission channel immediately rather than waiting for the next daily sweep (Spain SII: the AEAT submitter; France e-reporting: the French e-invoicing platform adapter) and its status moves submitted → filed as the authority answers (poll GET /reporting-batches/{id}, or subscribe to the reporting_batch.* and per-invoice webhooks). Entries the regime's channel cannot carry yet are kept back and the batch is flagged `needsAttention.code = TRANSPORT_HELD`; the rest is sent. Optimistic concurrency: send the batch's CURRENT `snapshotVersion` from a prior GET; if a record joined, was excluded, or another confirm landed in between, the call is refused 409 BATCH_STALE with `currentSnapshotVersion`; re-read the batch (the membership you approved may have changed) and confirm again. Requires a write-scoped key.",
        "tags": [
          "Reporting Batches"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "snapshotVersion"
                ],
                "properties": {
                  "snapshotVersion": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "The batch's current snapshotVersion as read from GET /reporting-batches/{id}. A mismatch is refused 409 BATCH_STALE."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Confirmed and handed off for submission — or already confirmed (`alreadyConfirmed: true`, idempotent no-op; a concurrent or earlier confirm won).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "batch"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    },
                    "alreadyConfirmed": {
                      "type": "boolean",
                      "description": "Present and true only when the batch had already been confirmed; nothing changed."
                    },
                    "batch": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "open",
                            "closed",
                            "ready_for_review",
                            "submitted",
                            "filed"
                          ]
                        },
                        "enqueued": {
                          "type": "boolean",
                          "description": "True when the batch was handed to the AEAT submitter in this call (absent on alreadyConfirmed)."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed `id`, missing/malformed `snapshotVersion` (a non-negative integer), or malformed JSON.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such batch, or it belongs to an entity this key is not scoped to (cross-entity access reads identically to not-found — never a distinct 403).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "BATCH_STALE: `snapshotVersion` does not match the batch's current version (a record joined, an exclude or another confirm already acted on it); the body carries `currentSnapshotVersion`, re-read the batch and confirm again. BATCH_STILL_OPEN: the batch's period has not ended yet, so it cannot be submitted; retry once the period has closed. ALREADY_SUBMITTED: the batch is already being submitted (a concurrent confirm, or the batch_auto sweep, has it in flight). TRANSMISSION_NOT_LIVE: the batch was confirmed but its dispatch channel is not live yet; it stays confirmed and is handed off automatically once the channel goes live.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "BATCH_STALE",
                        "BATCH_STILL_OPEN",
                        "ALREADY_SUBMITTED",
                        "TRANSMISSION_NOT_LIVE"
                      ]
                    },
                    "currentSnapshotVersion": {
                      "type": "integer",
                      "description": "BATCH_STALE only."
                    },
                    "hint": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "ENQUEUE_FAILED — the batch WAS confirmed but could not be handed to its submission channel right now. It stays confirmed and unlocked: it will be handed off automatically on the next scheduler run, or re-read GET /reporting-batches/{id} for its new snapshotVersion and confirm again to retry immediately.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "ENQUEUE_FAILED"
                      ]
                    },
                    "batch": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "enqueued": {
                          "type": "boolean",
                          "enum": [
                            false
                          ]
                        }
                      }
                    },
                    "hint": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Failed to confirm reporting batch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/reporting-batches/{id}/exclude": {
      "post": {
        "operationId": "excludeReportingBatchTransaction",
        "summary": "Exclude one record from a Spain SII reporting batch",
        "description": "Move one record (a `records[].id` from GET /reporting-batches/{id}) out of this batch and into the next open batch for the same entity, book and submission mode — for example a registro you want to hold back or correct before it is filed. `reason` is required and written to the audit trail; the source batch keeps listing the record as EXCLUDED (with `excludedBy`), the destination batch lists it as an ordinary member, and the source batch's deadline (`reportBy`) is recomputed from its remaining records. Bumps the batch's `snapshotVersion`, so a confirm prepared against the old version is refused BATCH_STALE — re-read before confirming. Only meaningful once the batch has closed (`closed`/`ready_for_review`): an `open` batch has no separate next batch to move into yet (409 BATCH_OPEN — the record can simply be corrected via Fix & Resubmit instead). Requires a write-scoped key.",
        "tags": [
          "Reporting Batches"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "transactionId",
                  "reason"
                ],
                "properties": {
                  "transactionId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "The record's `id` (mandate transaction id) from `records[]` on GET /reporting-batches/{id}. Must currently be a member of this batch — 400 otherwise."
                  },
                  "reason": {
                    "type": "string",
                    "description": "Why this record is being excluded — required, non-empty; stored on the audit trail and shown as `excludedBy.reason`."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Excluded and moved to the next open batch.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "movedToPeriodId",
                    "reportBy"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    },
                    "movedToPeriodId": {
                      "type": "string",
                      "format": "uuid",
                      "description": "The batch the record now belongs to."
                    },
                    "reportBy": {
                      "type": "string",
                      "format": "date",
                      "nullable": true,
                      "description": "The SOURCE batch's recomputed report-by deadline after the exclusion."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed id/transactionId/reason, or transactionId is not a member of this batch.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such batch, or it belongs to an entity this key is not scoped to (cross-entity access reads identically to not-found — never a distinct 403).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "BATCH_OPEN — the batch has not closed yet, so there is no other batch to move the record into. BATCH_LOCKED — the batch has been confirmed and is mid-submission; nothing can be excluded now. ALREADY_SUBMITTED — the batch is submitted/filed; nothing left to exclude from. EXCLUDE_WOULD_EMPTY_BATCH — this is the batch's last record; an empty batch can never be submitted, so leave the batch unconfirmed instead (nothing is sent to AEAT until you confirm it).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "BATCH_OPEN",
                        "BATCH_LOCKED",
                        "ALREADY_SUBMITTED",
                        "EXCLUDE_WOULD_EMPTY_BATCH"
                      ]
                    },
                    "hint": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Failed to exclude transaction from reporting batch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/test-helpers/reporting-batches/run-sweep": {
      "post": {
        "operationId": "runReportingBatchSweep",
        "summary": "Sandbox only: run the Spain SII batch close/dispatch/notify sweeps now",
        "description": "SANDBOX ONLY (csk_test_* key — a production key gets 403 SANDBOX_ONLY). Runs, for the calling entity only, the same three daily sweeps production runs overnight — close (open → closed, or straight to the ES_SII deadline trigger), dispatch (closed → ready_for_review for `batch_review`, or handed to the sandbox AEAT submitter for `batch_auto`), and the reporting_batch.* notification ladder — so you can drive a batch through its full lifecycle in a test: POST /send a few ES invoices → run-sweep → GET /reporting-batches (status ready_for_review) → POST .../confirm → GET again for outcomes, all without waiting for the real Spanish-business-day clock. When `today`/`madridToday` are omitted both default to this entity's own earliest open batch report-by date, which is what makes a batch created moments ago close immediately; pass explicit dates to exercise a specific scenario (e.g. the T-2/T-1 reminders, or an overdue batch).",
        "tags": [
          "Reporting Batches"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "today": {
                    "type": "string",
                    "format": "date",
                    "description": "Override for the generic close-sweep clock (YYYY-MM-DD). Omit to default to this entity's earliest open batch report-by date."
                  },
                  "madridToday": {
                    "type": "string",
                    "format": "date",
                    "description": "Override for the Europe/Madrid civil-date clock the ES_SII batch trigger and notify sweep gate on (YYYY-MM-DD). Omit to default the same way as today. Note: the sweeps only act on a Spanish business day."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The raw per-step results — each names every batch it touched and the outcome kind.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "today",
                    "madridToday",
                    "closeSweep",
                    "scheduler",
                    "batchNotify"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    },
                    "today": {
                      "type": "string",
                      "format": "date",
                      "nullable": true,
                      "description": "The clock actually used (null when the entity had no open batch and no override was passed)."
                    },
                    "madridToday": {
                      "type": "string",
                      "format": "date",
                      "nullable": true
                    },
                    "closeSweep": {
                      "type": "object",
                      "description": "Close-sweep result: counts (`closed`, `waiting`, …) plus a per-period list of outcomes.",
                      "additionalProperties": true
                    },
                    "scheduler": {
                      "type": "object",
                      "description": "Dispatch result: `readyForReview`, `enqueued`, `submitted`, `filed` counts plus `periods[]` with each batch's outcome (`ready_for_review` for batch_review, `enqueued` for batch_auto).",
                      "additionalProperties": true
                    },
                    "batchNotify": {
                      "type": "object",
                      "description": "Notify-sweep result: `processed`, `notified` (stage notices emitted — webhooks + emails), `errors`.",
                      "additionalProperties": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed today/madridToday (must be YYYY-MM-DD) or malformed JSON.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "SANDBOX_ONLY — the API key is not a sandbox (csk_test_*) key. Provision a sandbox entity and key and retry with that.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "SANDBOX_ONLY"
                      ]
                    },
                    "hint": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Failed to run the reporting-batch sweep",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/sii/reconciliation": {
      "get": {
        "operationId": "getSiiReconciliationSummary",
        "summary": "Latest AEAT Spain SII Consulta reconciliation run's id and condensed status",
        "description": "Spain SII block 3. Returns the current entity's latest AEAT Consulta reconciliation run id plus the same collapsed reassurance-line view the dashboard's own Spain SII page shows. Call this first to get an `id` for GET /sii/reconciliation/{id} below — this route has no other way to discover a run id. Read-only and scheduled (a Container Apps job) — there is no way to trigger a run on demand from here.",
        "tags": [
          "Reporting Batches"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "The latest run's id and condensed status.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "id",
                    "authorityView"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    },
                    "id": {
                      "type": "string",
                      "format": "uuid",
                      "nullable": true,
                      "description": "Null only when the Consulta sweep has never run for this entity."
                    },
                    "authorityView": {
                      "type": "object",
                      "required": [
                        "status",
                        "lastCheckedAt",
                        "matchedCount",
                        "mismatchCount"
                      ],
                      "properties": {
                        "status": {
                          "type": "string",
                          "enum": [
                            "never_checked",
                            "matched",
                            "mismatch"
                          ]
                        },
                        "lastCheckedAt": {
                          "type": "string",
                          "format": "date-time",
                          "nullable": true
                        },
                        "matchedCount": {
                          "type": "integer"
                        },
                        "mismatchCount": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/sii/reconciliation/{id}": {
      "get": {
        "operationId": "getSiiReconciliation",
        "summary": "Fetch one AEAT Spain SII Consulta reconciliation run",
        "description": "Spain SII block 3. Read-only, scheduled, never triggerable on demand — the AEAT Consulta sweep pages a period's registros by ClavePaginacion and compares them against this platform's own records: a sent-but-unanswered local record adopts AEAT's own state and CSV, a locally-ACCEPTED record AEAT does not hold at all is a platform-fault alert (`authorityView.missingAtAeat`, never silently un-accepted), and AEAT's own EstadoCuadre is stored as informational. `id` comes from GET /sii/reconciliation (no id, above) or the dashboard.",
        "tags": [
          "Reporting Batches"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The reconciliation run ID (a UUID)."
          }
        ],
        "responses": {
          "200": {
            "description": "The run's stored comparison result.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "run",
                    "authorityView"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    },
                    "run": {
                      "$ref": "#/components/schemas/SiiReconciliationRun"
                    },
                    "authorityView": {
                      "description": "The SAME run's counters restructured under a name that reads as authority-observed fact rather than a raw DB row — \"what AEAT's own Consulta showed us\". Same shape as `run` minus `id`/`entityId`/`book`/`ejercicio`/`periodo`.",
                      "$ref": "#/components/schemas/SiiAuthorityView"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`id` is missing or not a UUID.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such run, OR it exists but belongs to an entity this key is not scoped to — cross-entity access reads identically to \"not found\", same convention as GET /reporting-batches/{id}.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tax/reporting-obligations": {
      "get": {
        "operationId": "listReportingObligations",
        "summary": "List tax reporting obligations",
        "description": "List the per-regime reporting obligations for the caller's entity under the \"Tax Reporting\" solution — one row per customer-toggleable regime (today France e-reporting `fr_ereporting`, Spain SII `es_sii`, and Portugal invoice-data communication to AT `pt_efatura`). Every regime defaults to enabled=false when the entity has never set it — obligations are user-enabled with an explicit start date, never derived from registrations or invoice data. `registered` reports whether the entity holds a current STANDARD registration in that country; when false the row carries a `registrationGate` { message, fixUrl } and an enabling PATCH is refused 400 REGISTRATION_REQUIRED — send the user to fixUrl first. An ENABLED row whose registration has since lapsed instead carries `registrationWarning` (submissions are held until it is restored; the row is never auto-disabled). Each row also carries the regime's `allowedSubmissionModes` and `defaultSubmissionMode` (batch_review for es_sii and fr_ereporting), display labels/help, and `openBatch`/`modeChangeNotice` when a Spain SII batch is currently accumulating (see GET /v1/reporting-batches). `setupStatus`/`setupStatusNote` are an internal, ops-maintained progress note (e.g. AEAT SII census enrolment) — read-only on this surface. A `vocabulary` block lists every submission mode and setup status with labels/descriptions.",
        "tags": [
          "Tax Reporting"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "Reporting obligations",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "obligations": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ReportingObligation"
                      }
                    },
                    "vocabulary": {
                      "type": "object",
                      "description": "Labels for rendering: every submission mode and setup status.",
                      "properties": {
                        "submissionModes": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "code": {
                                "type": "string",
                                "enum": [
                                  "immediate",
                                  "batch_auto",
                                  "batch_review"
                                ]
                              },
                              "label": {
                                "type": "string"
                              },
                              "description": {
                                "type": "string"
                              }
                            }
                          }
                        },
                        "setupStatuses": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "code": {
                                "type": "string"
                              },
                              "label": {
                                "type": "string"
                              },
                              "nextStep": {
                                "type": "string",
                                "nullable": true
                              },
                              "tone": {
                                "type": "string",
                                "enum": [
                                  "neutral",
                                  "warning",
                                  "info",
                                  "success",
                                  "danger"
                                ]
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Failed to fetch reporting obligations",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "operationId": "updateReportingObligations",
        "summary": "Update tax reporting obligations",
        "description": "Write one or more regime rows for the caller's entity. Each value under `obligations` is either a bare boolean (shorthand for `{ enabled }` — only ever succeeds for a disable) or an object `{ enabled, effectiveFrom?, submissionMode? }`. Enabling a regime REQUIRES `effectiveFrom` (ISO date, YYYY-MM-DD — the date the obligation starts applying); a bare `true` or an object without it is rejected with code EFFECTIVE_FROM_REQUIRED. `submissionMode` must be one the regime allows (es_sii and fr_ereporting: `immediate`, `batch_auto`, `batch_review`; a first enable with none supplied starts on the regime's default, `batch_review` — nothing is sent until you confirm the batch; pt_efatura: `immediate` only) — code SUBMISSION_MODE_NOT_ALLOWED otherwise. Enabling es_sii, fr_ereporting or pt_efatura also requires the entity to already hold a current STANDARD tax registration for that regime's country — otherwise 400 REGISTRATION_REQUIRED with a RegistrationRequiredError body ({ ok:false, code, message, country, entityId, fixUrl }); add the registration (POST /tax/registrations, or the dashboard page at fixUrl) and retry. Disabling is never gated on registration, but disabling es_sii while an open, ready_for_review, or overdue Spain SII batch exists is refused 409 OBLIGATION_HAS_PENDING_BATCH ({ batchIds, reportByDates, fixUrl }) unless `force: true` is passed — the batch itself is left untouched either way and still carries its real AEAT deadline. Changing es_sii's submissionMode never affects a batch already open; it applies from the next batch. Any regime code known to the platform is accepted as a key (e.g. `es_sii`, `es_verifactu`, `fr_ereporting`), not only the rows GET surfaces — an unknown key is rejected with code UNKNOWN_REGIME. A single invalid entry rejects the whole request; nothing is partially applied. `setupStatus`/`setupStatusNote` are ops-only: setting them requires the internal `x-admin-key` header and is rejected with SETUP_STATUS_FORBIDDEN otherwise. Side effects: enabling at least one regime activates the entity's \"Tax Reporting\" solution if it was off (a headless integration's \"Get started\" click) and resumes any transaction previously held as OBLIGATION_DISABLED for that regime. Enabling `es_sii` while `es_verifactu` is (or becomes) enabled succeeds but returns a `warnings[]` entry with code SII_EXEMPTS_VERIFACTU — an entity reporting under SII is exempt from VeriFactu, so the VeriFactu row is redundant. Set confirm=true to mark the getting-started tax-reporting step complete even when no obligations are changed (defaults reviewed and kept).",
        "tags": [
          "Tax Reporting"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "x-admin-key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Internal ops key — required only to set setupStatus/setupStatusNote. Not available to customer integrations."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "obligations": {
                    "type": "object",
                    "description": "Map of regime code (e.g. es_sii, es_verifactu, fr_ereporting, pt_efatura) to the desired row. A bare boolean is shorthand for { enabled } and only ever succeeds for a disable, since enabling requires an explicit effectiveFrom.",
                    "additionalProperties": {
                      "$ref": "#/components/schemas/ReportingObligationPatch"
                    },
                    "example": {
                      "es_sii": {
                        "enabled": true,
                        "effectiveFrom": "2026-10-01",
                        "submissionMode": "batch_review"
                      },
                      "fr_ereporting": false
                    }
                  },
                  "confirm": {
                    "type": "boolean",
                    "description": "Also stamp the entity's tax-reporting confirmation, even when obligations is empty."
                  },
                  "force": {
                    "type": "boolean",
                    "description": "Disable es_sii even though a reporting batch still needs attention (bypasses the 409 OBLIGATION_HAS_PENDING_BATCH). The pending batch is left completely untouched — confirm with the user first."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Obligations updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "obligations": {
                      "type": "object",
                      "description": "Map of every regime code this request touched to its enabled state after the write. Call GET for the full row (effectiveFrom, submissionMode, setupStatus).",
                      "additionalProperties": {
                        "type": "boolean"
                      }
                    },
                    "warnings": {
                      "type": "array",
                      "description": "Present only when a non-blocking warning applies. Today the single code is SII_EXEMPTS_VERIFACTU (es_sii and es_verifactu both enabled).",
                      "items": {
                        "type": "object",
                        "properties": {
                          "code": {
                            "type": "string",
                            "enum": [
                              "SII_EXEMPTS_VERIFACTU"
                            ]
                          },
                          "message": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed request. Every rejected obligations entry carries a machine-readable `code`: UNKNOWN_REGIME (unrecognised regime key or malformed value), EFFECTIVE_FROM_REQUIRED (enabling without effectiveFrom), SUBMISSION_MODE_NOT_ALLOWED (mode the regime does not allow), SETUP_STATUS_FORBIDDEN (setupStatus/setupStatusNote without x-admin-key), INVALID_SETUP_STATUS (unknown setupStatus value), INVALID_EFFECTIVE_FROM (not a real YYYY-MM-DD date), INVALID_SETUP_STATUS_NOTE, MULTI_COUNTRY_REGIME_NOT_SUPPORTED (a regime spanning more than one country, e.g. Peppol, has no single country this route can derive from the regime alone — enable it via the entity's Jurisdictions settings instead). REGISTRATION_REQUIRED (enabling fr_ereporting/es_sii with no current registration in that country) uses the richer RegistrationRequiredError body — { ok:false, code, message, country, entityId, fixUrl } — instead of the flat Error shape. \"No obligations provided\" (empty obligations without confirm) and invalid JSON carry no code.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "$ref": "#/components/schemas/RegistrationRequiredError"
                    }
                  ]
                }
              }
            },
            "x-errorCodes": [
              "UNKNOWN_REGIME",
              "EFFECTIVE_FROM_REQUIRED",
              "SUBMISSION_MODE_NOT_ALLOWED",
              "SETUP_STATUS_FORBIDDEN",
              "INVALID_SETUP_STATUS",
              "INVALID_EFFECTIVE_FROM",
              "INVALID_SETUP_STATUS_NOTE",
              "MULTI_COUNTRY_REGIME_NOT_SUPPORTED",
              "REGISTRATION_REQUIRED"
            ]
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "OBLIGATION_HAS_PENDING_BATCH — the request disables es_sii while the entity still has a Spain SII reporting batch that needs attention (open, ready_for_review, or past its report-by date). Re-send with `force: true` to disable anyway; the batch is left untouched and, if ready_for_review, remains confirmable via POST /v1/reporting-batches/{id}/confirm.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObligationHasPendingBatchError"
                }
              }
            }
          },
          "500": {
            "description": "Failed to update reporting obligations",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/products": {
      "get": {
        "operationId": "listCatalogProducts",
        "summary": "List catalogue products",
        "description": "Paginated list of catalogue products for the account — reusable product/service records that can be referenced from invoice line items, distinct from the tax classification records under /tax/products (same underlying tax_products table, shorter generic-REST path).",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "entityId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Filter by entity (organisation-scoped keys only)."
          },
          {
            "name": "sort",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "newest",
                "name",
                "confidence"
              ],
              "default": "newest"
            },
            "description": "newest (default) orders by created_at DESC; name orders alphabetically; confidence orders by AI classification confidence, highest first."
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 25,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "List of catalogue products",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "products": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CatalogProduct"
                      }
                    },
                    "pagination": {
                      "type": "object",
                      "properties": {
                        "page": {
                          "type": "integer"
                        },
                        "limit": {
                          "type": "integer"
                        },
                        "total": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "operationId": "createCatalogProduct",
        "summary": "Create a catalogue product",
        "description": "Create a reusable catalogue product/service record, with optional AI tax classification when taxCategory is omitted. If sku is provided and a product with the same (entityId, sku) already exists for the account, that record is updated in place instead of creating a duplicate — sku is the idempotency key, scoped per entity; a null sku always creates a new row. Setting taxCategory explicitly marks the record MANUAL — a later automatic reclassification (e.g. from a subsequent upsert with no taxCategory) never silently overwrites a human-set value.",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "example": "Annual SaaS subscription"
                  },
                  "sku": {
                    "type": "string",
                    "description": "Your own product SKU or identifier. Used as the idempotency key, scoped per entityId — re-posting the same sku updates the record instead of creating a duplicate.",
                    "example": "SKU-001"
                  },
                  "description": {
                    "type": "string",
                    "example": "Cloud-based ERP software, billed annually."
                  },
                  "taxCategory": {
                    "type": "string",
                    "description": "Explicit tax category slug override. Omit to let the AI classifier assign one from name/description/sku.",
                    "example": "saas_business"
                  },
                  "entityId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Organisation-scoped keys only — which entity this product belongs to. Ignored (the key's own entity always wins) for entity-scoped keys. Omit for an account-wide product not tied to one entity."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "400": {
            "description": "entityId does not belong to this account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the entity specified in entityId.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "name is required, or the explicit taxCategory override names a tax category that has been deleted (error tax_category_deleted, with canonicalTaxCategory when a replacement exists) or does not exist at all (error tax_category_unknown).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "201": {
            "description": "Product created (or updated in place, if sku matched an existing record).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "product": {
                      "$ref": "#/components/schemas/CatalogProduct"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/products/{id}": {
      "get": {
        "operationId": "getCatalogProduct",
        "summary": "Get a catalogue product",
        "description": "Fetch a single catalogue product by ID.",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Product ID (tp_ prefix)."
          }
        ],
        "responses": {
          "200": {
            "description": "Catalogue product",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "product": {
                      "$ref": "#/components/schemas/CatalogProduct"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Product not found, or not owned by this account/entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "operationId": "updateCatalogProduct",
        "summary": "Update a catalogue product",
        "description": "Update name, sku, description, or taxCategory on a catalogue product. At least one field is required.",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Product ID (tp_ prefix)."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "All fields optional individually, but at least one is required.",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "sku": {
                    "type": "string"
                  },
                  "description": {
                    "type": "string"
                  },
                  "taxCategory": {
                    "type": "string",
                    "description": "Setting this marks the record MANUAL and re-approves it (status becomes APPROVED)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "400": {
            "description": "Invalid JSON body",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Product not found, or not owned by this account/entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "No updatable field was provided (at least one of name, sku, description, taxCategory is required), or taxCategory names a tax category that has been deleted (error tax_category_deleted, with canonicalTaxCategory when a replacement exists) or does not exist at all (error tax_category_unknown).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Updated product",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "product": {
                      "$ref": "#/components/schemas/CatalogProduct"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "deleteCatalogProduct",
        "summary": "Delete a catalogue product",
        "description": "Soft-delete (sets deleted_at) — the product no longer appears in GET /products or GET /products/{id}.",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Product ID (tp_ prefix)."
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Product not found, not owned by this account/entity, or already deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tax/products": {
      "get": {
        "operationId": "listProducts",
        "summary": "List classified products",
        "description": "Paginated list of tax product classifications for one entity: an entity-scoped key's own entity, or the entity an organisation-scoped key names in x-entity-id (plus that account's entity-less, account-wide rows for an unrestricted organisation key). Products are classified automatically by AI when created, and can be manually reviewed and approved. A single call never spans more than one entity.",
        "tags": [
          "Tax Calculations"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "OAuthBearer": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "APPROVED",
                "NEEDS_REVIEW",
                "MANUAL"
              ]
            },
            "description": "Filter by classification status."
          },
          {
            "name": "entityId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Filter by entity (organisation-scoped keys only)."
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 25,
              "maximum": 100
            }
          },
          {
            "name": "sort",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "newest",
                "name",
                "confidence"
              ],
              "default": "newest"
            },
            "description": "Sort order. newest = created_at descending (default). name = product name A–Z. confidence = highest AI classification confidence first; products without a real AI confidence (confirmed, standard-mapped, or unclassified) sort last."
          }
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "List of products",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "products": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Product"
                      }
                    },
                    "pagination": {
                      "type": "object",
                      "properties": {
                        "page": {
                          "type": "integer"
                        },
                        "limit": {
                          "type": "integer"
                        },
                        "total": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "operationId": "createProduct",
        "summary": "Classify a product",
        "description": "Classify a single product for tax purposes. Clearvo uses AI to assign a tax category slug based on productName and productDescription. Pass taxCategory to override with an explicit slug (sets status to APPROVED immediately). If a product with the same productCode already exists for the account, it is updated in place.",
        "tags": [
          "Tax Calculations"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "productCode": {
                    "type": "string",
                    "description": "Your product SKU or identifier. Used as the idempotency key — re-posting the same code updates the record.",
                    "example": "SKU-001"
                  },
                  "productName": {
                    "type": "string",
                    "example": "Annual SaaS subscription"
                  },
                  "productDescription": {
                    "type": "string",
                    "example": "Cloud-based ERP software, billed annually."
                  },
                  "taxCategory": {
                    "type": "string",
                    "description": "Explicit tax category slug override. Bypasses AI classification and sets status=APPROVED.",
                    "example": "saas"
                  },
                  "classificationCodes": {
                    "type": "array",
                    "maxItems": 5,
                    "items": {
                      "$ref": "#/components/schemas/ClassificationCode"
                    },
                    "description": "Outside product codes for this product (Stripe tax code, Shopify taxonomy category, customs commodity code, ...), up to 5, tried in order; the first that maps to a Clearvo category is used. Ambiguous, unmappable or unknown codes are skipped.",
                    "example": [
                      {
                        "system": "shopify",
                        "code": "aa-1-13"
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "201": {
            "description": "Product classified",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "product": {
                      "$ref": "#/components/schemas/Product"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing productCode and productName",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The explicit taxCategory override names a tax category that has been deleted (error tax_category_deleted, with canonicalTaxCategory when a replacement exists) or does not exist at all (error tax_category_unknown).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tax/products/{id}": {
      "patch": {
        "operationId": "updateProduct",
        "summary": "Update product classification",
        "description": "Update the tax category slug or review status for a classified product. Sets classificationSource to MANUAL.",
        "tags": [
          "Tax Calculations"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Product ID (tp_ prefix)."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "slug": {
                    "type": "string",
                    "description": "New tax category slug.",
                    "example": "food_basic"
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "APPROVED",
                      "NEEDS_REVIEW",
                      "MANUAL"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Updated product",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "product": {
                      "$ref": "#/components/schemas/Product"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid status value",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Product not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "slug names a tax category that has been deleted (error tax_category_deleted, with canonicalTaxCategory when a replacement exists) or does not exist at all (error tax_category_unknown).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "deleteProduct",
        "summary": "Delete a product classification",
        "description": "Soft-delete a product classification. The record is retained for audit purposes but excluded from future tax calculations.",
        "tags": [
          "Tax Calculations"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Product ID (tp_ prefix)."
          }
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Product deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Product not found or already deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tax/products/{id}/restore": {
      "post": {
        "operationId": "restoreProduct",
        "summary": "Restore a deleted product classification",
        "description": "Restore a previously soft-deleted product classification, making it eligible for tax calculations again.",
        "tags": [
          "Tax Calculations"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Product ID (tp_ prefix)."
          }
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Product restored",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "product": {
                      "$ref": "#/components/schemas/Product"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Product not found or not currently deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tax/products/bulk": {
      "post": {
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "operationId": "bulkClassifyProducts",
        "summary": "Bulk classify products",
        "description": "Classify up to 500 products in one request. Accepts either a JSON body (`{ products: [...] }`) or a CSV upload (`Content-Type: text/csv`) with header row: `product_code,product_name,product_description,tax_category`. Products with the same productCode are upserted. Per-item errors are returned inline.",
        "tags": [
          "Tax Calculations"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "products"
                ],
                "properties": {
                  "products": {
                    "type": "array",
                    "maxItems": 500,
                    "items": {
                      "type": "object",
                      "properties": {
                        "productCode": {
                          "type": "string"
                        },
                        "productName": {
                          "type": "string"
                        },
                        "productDescription": {
                          "type": "string"
                        },
                        "taxCategory": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            },
            "text/csv": {
              "schema": {
                "type": "string",
                "description": "CSV with header: product_code,product_name,product_description,tax_category"
              }
            }
          }
        },
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Bulk classification results",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "queued": {
                      "type": "integer",
                      "description": "Number of rows processed."
                    },
                    "summary": {
                      "type": "object",
                      "properties": {
                        "succeeded": {
                          "type": "integer"
                        },
                        "failed": {
                          "type": "integer"
                        }
                      }
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "productCode": {
                            "type": "string",
                            "nullable": true
                          },
                          "productName": {
                            "type": "string",
                            "nullable": true
                          },
                          "slug": {
                            "type": "string",
                            "nullable": true
                          },
                          "confidence": {
                            "type": "number",
                            "nullable": true
                          },
                          "status": {
                            "type": "string",
                            "nullable": true
                          },
                          "error": {
                            "type": "string",
                            "description": "Present only when this item failed.",
                            "nullable": true
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "No valid rows, too many rows, or invalid JSON",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tax/settings": {
      "get": {
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "operationId": "getTaxSettings",
        "summary": "Get tax calculation settings",
        "description": "Read the account-level tax calculation settings, plus the entity-grain-only defaultImporterOfRecord/importVatAccountingElection purchase-side settings (no account-level tier for either). Also returns the list of available tax category slugs.",
        "tags": [
          "Tax Calculations"
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Current settings",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "settings": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/TaxSettings"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "availableTaxCategories": {
                              "type": "array",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "slug": {
                                    "type": "string"
                                  },
                                  "name": {
                                    "type": "string"
                                  }
                                }
                              }
                            }
                          }
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "operationId": "updateTaxSettings",
        "summary": "Update tax calculation settings",
        "description": "Update one or more account-level tax calculation settings. All fields are optional — omit any field to leave it unchanged.",
        "tags": [
          "Tax Calculations"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TaxSettings"
              }
            }
          }
        },
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Updated settings",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "settings": {
                      "$ref": "#/components/schemas/TaxSettings"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid field value",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "defaultTaxCategorySlug names a tax category that has been deleted (error tax_category_deleted, with canonicalTaxCategory when a replacement exists) or does not exist at all (error tax_category_unknown).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tax/categories": {
      "get": {
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "operationId": "listTaxCategories",
        "summary": "List tax categories",
        "description": "List all active tax category slugs. Use these slugs in lineItems.taxCategory on calculate requests, in POST /v1/tax/products for explicit classification, or as the defaultTaxCategorySlug in account settings.",
        "tags": [
          "Tax Calculations"
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "List of tax categories",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "example": "list"
                    },
                    "categories": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TaxCategory"
                      }
                    },
                    "count": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/mandates": {
      "get": {
        "operationId": "listMandates",
        "summary": "List supported e-invoicing mandates and their required fields",
        "description": "Static reference data describing every e-invoicing mandate Clearvo supports: authority, status, accepted input formats, document types, currencies, typical submission latency, and (in the detail view) the mandate-specific required fields to validate payloads against before calling /send. Any valid API key with e-invoicing read access may call this.",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Return a single mandate with its full requiredFields list (e.g. IT-SDI). Omit to list all mandates (requiredFields omitted for brevity)."
          }
        ],
        "responses": {
          "200": {
            "description": "Mandate list, or a single mandate when id is given.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "mandates": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Mandate"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key."
          },
          "403": {
            "description": "The API key lacks e-invoicing read access."
          },
          "404": {
            "description": "Unknown mandate id."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/rules": {
      "get": {
        "operationId": "listEntityRules",
        "summary": "List this entity's active validation rules",
        "description": "Every entity-scoped validation rule for the resolved entity, each carrying its condition/transform definition plus applied-count/last-applied evidence — parity with the dashboard's own entity-rules view.",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "This entity's rules.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "rules": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/EntityRule"
                      }
                    }
                  },
                  "required": [
                    "rules"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Failed to list entity rules",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "operationId": "createEntityRule",
        "summary": "Author a custom rule (removed)",
        "description": "Custom rule authoring against the generic Custom Rule Model (CDM) engine has been retired entirely. This method is kept documented, rather than left to 404, so an integrator that still calls it gets an explicit, permanent 405 instead of an ambiguous failure. Existing entity rules remain viewable (GET /v1/rules) and one-click deactivatable (DELETE /v1/rules/{ruleCode}).",
        "tags": [
          "E-Invoicing"
        ],
        "responses": {
          "405": {
            "description": "Always returned — custom rule authoring is retired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "CUSTOM_RULE_AUTHORING_REMOVED"
                      ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/rules/{ruleCode}": {
      "get": {
        "operationId": "getEntityRule",
        "summary": "Get a single entity rule",
        "description": "Same fields as one entry of GET /v1/rules, minus the applied-count/last-applied evidence (which that list route already computes and this single-rule lookup does not).",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "ruleCode",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The rule.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "rule": {
                      "$ref": "#/components/schemas/EntityRuleBase"
                    }
                  },
                  "required": [
                    "rule"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No rule with this ruleCode for this entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Failed to load entity rule",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "operationId": "updateEntityRule",
        "summary": "Edit a custom rule (removed)",
        "description": "Custom rule authoring against the generic Custom Rule Model (CDM) engine has been retired entirely — same retirement as POST /v1/rules. Always returns 405.",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "name": "ruleCode",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "405": {
            "description": "Always returned — custom rule authoring is retired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "CUSTOM_RULE_AUTHORING_REMOVED"
                      ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "deactivateEntityRule",
        "summary": "One-click deactivate a rule for this entity",
        "description": "Sets enabled: false on the rule, prospective-only (past evaluations stand) and immediate, leaving every other column untouched. This is the only way to turn off a single rule outright.",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "ruleCode",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The deactivated rule.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "rule": {
                      "$ref": "#/components/schemas/EntityRuleBase"
                    }
                  },
                  "required": [
                    "rule"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No rule with this ruleCode for this entity (code NOT_FOUND).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Failed to deactivate entity rule",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tax/jurisdictions": {
      "get": {
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "operationId": "listTaxJurisdictions",
        "summary": "List supported tax jurisdictions",
        "description": "List every jurisdiction the tax calculation engine can calculate tax for, plus any announced jurisdictions that are not yet live (comingSoon: true). Coverage discovery only — this endpoint never returns rates or rate bands. When POST /v1/tax/calculate resolves a transaction to a country that is not supported it does not fail: it returns 200 with totalTax 0, taxTreatment UNSUPPORTED_JURISDICTION, an unsupportedJurisdiction field and a country_not_supported warning whose hint points callers here. Entries are sorted alphabetically by ISO 3166-1 alpha-2 country code. Consistency bound: this endpoint reads coverage live on every request, while the calculate pipeline caches coverage for up to 30 minutes — a jurisdiction newly appearing here can take up to 30 minutes to be accepted by calculate. Recommended client pattern: cache this list and refresh it every 30–60 minutes, and re-fetch when a response carries unsupportedJurisdiction or a country_not_supported warning. Coverage is additive-only: a jurisdiction that is live (comingSoon: false) today is never later removed from this list or dropped from calculate support, so it is safe to treat this list as monotonically growing when caching it client-side or hardcoding a known-supported set.",
        "tags": [
          "Tax Calculations"
        ],
        "responses": {
          "200": {
            "description": "List of supported jurisdictions, sorted alphabetically by ISO country code",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "type": "string",
                      "example": "list"
                    },
                    "jurisdictions": {
                      "type": "array",
                      "description": "Sorted alphabetically by country code. Disabled jurisdictions are omitted entirely — absence from this list means POST /v1/tax/calculate returns 200 with totalTax 0 and taxTreatment UNSUPPORTED_JURISDICTION for that country (not an error).",
                      "items": {
                        "type": "object",
                        "required": [
                          "country",
                          "name",
                          "comingSoon"
                        ],
                        "properties": {
                          "country": {
                            "type": "string",
                            "description": "ISO 3166-1 alpha-2 country code (uppercase).",
                            "example": "DE"
                          },
                          "name": {
                            "type": "string",
                            "description": "English display name of the jurisdiction.",
                            "example": "Germany"
                          },
                          "comingSoon": {
                            "type": "boolean",
                            "description": "false — live now: calculate accepts this jurisdiction. true — announced but not yet live: calculate still returns the 200 zero-tax UNSUPPORTED_JURISDICTION result until the availableFrom date, at which point the jurisdiction normally goes live automatically (no further announcement, no API change). If a go-live is postponed, the entry simply remains comingSoon: true — treat this field, not the date, as authoritative."
                          },
                          "availableFrom": {
                            "type": "string",
                            "format": "date",
                            "description": "Present only when comingSoon is true — the announced go-live date (YYYY-MM-DD). Omitted entirely on live jurisdictions.",
                            "example": "2026-10-01"
                          }
                        }
                      }
                    },
                    "count": {
                      "type": "integer",
                      "description": "Total number of entries in jurisdictions (live plus coming soon).",
                      "example": 76
                    }
                  }
                },
                "examples": {
                  "current": {
                    "summary": "Full current response (all 76 jurisdictions live, none announced)",
                    "value": {
                      "object": "list",
                      "jurisdictions": [
                        {
                          "country": "AE",
                          "name": "United Arab Emirates",
                          "comingSoon": false
                        },
                        {
                          "country": "AL",
                          "name": "Albania",
                          "comingSoon": false
                        },
                        {
                          "country": "AR",
                          "name": "Argentina",
                          "comingSoon": false
                        },
                        {
                          "country": "AT",
                          "name": "Austria",
                          "comingSoon": false
                        },
                        {
                          "country": "AU",
                          "name": "Australia",
                          "comingSoon": false
                        },
                        {
                          "country": "BA",
                          "name": "Bosnia & Herzegovina",
                          "comingSoon": false
                        },
                        {
                          "country": "BD",
                          "name": "Bangladesh",
                          "comingSoon": false
                        },
                        {
                          "country": "BE",
                          "name": "Belgium",
                          "comingSoon": false
                        },
                        {
                          "country": "BG",
                          "name": "Bulgaria",
                          "comingSoon": false
                        },
                        {
                          "country": "BH",
                          "name": "Bahrain",
                          "comingSoon": false
                        },
                        {
                          "country": "BR",
                          "name": "Brazil",
                          "comingSoon": false
                        },
                        {
                          "country": "CA",
                          "name": "Canada",
                          "comingSoon": false
                        },
                        {
                          "country": "CH",
                          "name": "Switzerland",
                          "comingSoon": false
                        },
                        {
                          "country": "CL",
                          "name": "Chile",
                          "comingSoon": false
                        },
                        {
                          "country": "CN",
                          "name": "China",
                          "comingSoon": false
                        },
                        {
                          "country": "CO",
                          "name": "Colombia",
                          "comingSoon": false
                        },
                        {
                          "country": "CY",
                          "name": "Cyprus",
                          "comingSoon": false
                        },
                        {
                          "country": "CZ",
                          "name": "Czechia",
                          "comingSoon": false
                        },
                        {
                          "country": "DE",
                          "name": "Germany",
                          "comingSoon": false
                        },
                        {
                          "country": "DK",
                          "name": "Denmark",
                          "comingSoon": false
                        },
                        {
                          "country": "EC",
                          "name": "Ecuador",
                          "comingSoon": false
                        },
                        {
                          "country": "EE",
                          "name": "Estonia",
                          "comingSoon": false
                        },
                        {
                          "country": "EG",
                          "name": "Egypt",
                          "comingSoon": false
                        },
                        {
                          "country": "ES",
                          "name": "Spain",
                          "comingSoon": false
                        },
                        {
                          "country": "FI",
                          "name": "Finland",
                          "comingSoon": false
                        },
                        {
                          "country": "FR",
                          "name": "France",
                          "comingSoon": false
                        },
                        {
                          "country": "GB",
                          "name": "United Kingdom",
                          "comingSoon": false
                        },
                        {
                          "country": "GH",
                          "name": "Ghana",
                          "comingSoon": false
                        },
                        {
                          "country": "GR",
                          "name": "Greece",
                          "comingSoon": false
                        },
                        {
                          "country": "HK",
                          "name": "Hong Kong SAR China",
                          "comingSoon": false
                        },
                        {
                          "country": "HR",
                          "name": "Croatia",
                          "comingSoon": false
                        },
                        {
                          "country": "HU",
                          "name": "Hungary",
                          "comingSoon": false
                        },
                        {
                          "country": "ID",
                          "name": "Indonesia",
                          "comingSoon": false
                        },
                        {
                          "country": "IE",
                          "name": "Ireland",
                          "comingSoon": false
                        },
                        {
                          "country": "IL",
                          "name": "Israel",
                          "comingSoon": false
                        },
                        {
                          "country": "IN",
                          "name": "India",
                          "comingSoon": false
                        },
                        {
                          "country": "IS",
                          "name": "Iceland",
                          "comingSoon": false
                        },
                        {
                          "country": "IT",
                          "name": "Italy",
                          "comingSoon": false
                        },
                        {
                          "country": "JO",
                          "name": "Jordan",
                          "comingSoon": false
                        },
                        {
                          "country": "JP",
                          "name": "Japan",
                          "comingSoon": false
                        },
                        {
                          "country": "KE",
                          "name": "Kenya",
                          "comingSoon": false
                        },
                        {
                          "country": "KR",
                          "name": "South Korea",
                          "comingSoon": false
                        },
                        {
                          "country": "KW",
                          "name": "Kuwait",
                          "comingSoon": false
                        },
                        {
                          "country": "LT",
                          "name": "Lithuania",
                          "comingSoon": false
                        },
                        {
                          "country": "LU",
                          "name": "Luxembourg",
                          "comingSoon": false
                        },
                        {
                          "country": "LV",
                          "name": "Latvia",
                          "comingSoon": false
                        },
                        {
                          "country": "MA",
                          "name": "Morocco",
                          "comingSoon": false
                        },
                        {
                          "country": "ME",
                          "name": "Montenegro",
                          "comingSoon": false
                        },
                        {
                          "country": "MK",
                          "name": "North Macedonia",
                          "comingSoon": false
                        },
                        {
                          "country": "MT",
                          "name": "Malta",
                          "comingSoon": false
                        },
                        {
                          "country": "MX",
                          "name": "Mexico",
                          "comingSoon": false
                        },
                        {
                          "country": "MY",
                          "name": "Malaysia",
                          "comingSoon": false
                        },
                        {
                          "country": "NG",
                          "name": "Nigeria",
                          "comingSoon": false
                        },
                        {
                          "country": "NL",
                          "name": "Netherlands",
                          "comingSoon": false
                        },
                        {
                          "country": "NO",
                          "name": "Norway",
                          "comingSoon": false
                        },
                        {
                          "country": "NZ",
                          "name": "New Zealand",
                          "comingSoon": false
                        },
                        {
                          "country": "OM",
                          "name": "Oman",
                          "comingSoon": false
                        },
                        {
                          "country": "PE",
                          "name": "Peru",
                          "comingSoon": false
                        },
                        {
                          "country": "PH",
                          "name": "Philippines",
                          "comingSoon": false
                        },
                        {
                          "country": "PL",
                          "name": "Poland",
                          "comingSoon": false
                        },
                        {
                          "country": "PT",
                          "name": "Portugal",
                          "comingSoon": false
                        },
                        {
                          "country": "RO",
                          "name": "Romania",
                          "comingSoon": false
                        },
                        {
                          "country": "RS",
                          "name": "Serbia",
                          "comingSoon": false
                        },
                        {
                          "country": "SA",
                          "name": "Saudi Arabia",
                          "comingSoon": false
                        },
                        {
                          "country": "SE",
                          "name": "Sweden",
                          "comingSoon": false
                        },
                        {
                          "country": "SG",
                          "name": "Singapore",
                          "comingSoon": false
                        },
                        {
                          "country": "SI",
                          "name": "Slovenia",
                          "comingSoon": false
                        },
                        {
                          "country": "SK",
                          "name": "Slovakia",
                          "comingSoon": false
                        },
                        {
                          "country": "TH",
                          "name": "Thailand",
                          "comingSoon": false
                        },
                        {
                          "country": "TR",
                          "name": "Türkiye",
                          "comingSoon": false
                        },
                        {
                          "country": "TW",
                          "name": "Taiwan",
                          "comingSoon": false
                        },
                        {
                          "country": "UA",
                          "name": "Ukraine",
                          "comingSoon": false
                        },
                        {
                          "country": "US",
                          "name": "United States",
                          "comingSoon": false
                        },
                        {
                          "country": "UY",
                          "name": "Uruguay",
                          "comingSoon": false
                        },
                        {
                          "country": "VN",
                          "name": "Vietnam",
                          "comingSoon": false
                        },
                        {
                          "country": "ZA",
                          "name": "South Africa",
                          "comingSoon": false
                        }
                      ],
                      "count": 76
                    }
                  },
                  "withComingSoon": {
                    "summary": "Shape when a jurisdiction is announced but not yet live — illustrative only (no jurisdiction is currently in this state; shown as if Qatar enacted its VAT law)",
                    "value": {
                      "object": "list",
                      "jurisdictions": [
                        {
                          "country": "AE",
                          "name": "United Arab Emirates",
                          "comingSoon": false
                        },
                        {
                          "country": "AL",
                          "name": "Albania",
                          "comingSoon": false
                        },
                        {
                          "country": "QA",
                          "name": "Qatar",
                          "comingSoon": true,
                          "availableFrom": "2027-01-01"
                        }
                      ],
                      "count": 3
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/setup/status": {
      "get": {
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "operationId": "getSetupStatus",
        "summary": "Get account setup status",
        "description": "Returns what's left to finish setting up this account — the same checklist the dashboard's Getting Started widget shows, reachable over the public API for AI agents and integrations. Steps not applicable to this account (e.g. team invites on a Starter plan, or product-catalogue review when Tax Calculations is disabled) are omitted entirely.",
        "tags": [
          "Setup"
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Current setup status",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "enabledSolutions": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "plan": {
                      "type": "string",
                      "nullable": true,
                      "enum": [
                        "starter",
                        "business",
                        "enterprise"
                      ]
                    },
                    "steps": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "step": {
                            "type": "string",
                            "example": "tax-settings"
                          },
                          "done": {
                            "type": "boolean"
                          },
                          "gatedBy": {
                            "type": "string",
                            "nullable": true,
                            "description": "Solution code or plan condition that makes this step applicable, or null if always shown."
                          },
                          "required": {
                            "type": "boolean",
                            "description": "False for steps with a safe fallback or no public API (product catalogue, team invites, ECM certificates, tax reporting) — an incomplete optional step does not block readyForLaunch."
                          },
                          "description": {
                            "type": "string",
                            "description": "What this step means."
                          },
                          "howTo": {
                            "type": "string",
                            "description": "Which tool to call, or which dashboard page to visit, to complete this step."
                          },
                          "applicable": {
                            "type": "boolean"
                          }
                        }
                      }
                    },
                    "nextSteps": {
                      "type": "array",
                      "description": "One entry per enabled solution that has a step-by-step integration guide — points to how to actually wire the API into your application once setup is done. Not gated on 100% checklist completion. Solutions without a guide yet (e.g. TIN validation) are simply omitted.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "solution": {
                            "type": "string",
                            "example": "tax_calculations"
                          },
                          "title": {
                            "type": "string",
                            "example": "Integrate tax calculation into your checkout"
                          },
                          "guideUrl": {
                            "type": "string",
                            "format": "uri",
                            "example": "https://clearvo.io/docs/integrate-tax-calculations"
                          },
                          "summary": {
                            "type": "string",
                            "description": "One or two sentences on the integration flow, enough to act on without following the link."
                          }
                        }
                      }
                    },
                    "readyForLaunch": {
                      "type": "boolean",
                      "description": "True once every required, applicable step is done AND — when Tax Calculations is enabled — at least one registration is actively collecting tax (collectFromDate set and in the past)."
                    },
                    "blockedReason": {
                      "type": "string",
                      "description": "Present only when every required step shows done but readyForLaunch is still false — explains that a registration exists but was never activated for collection."
                    },
                    "launchGuidance": {
                      "type": "object",
                      "description": "Present only when readyForLaunch is true.",
                      "properties": {
                        "sandbox": {
                          "type": "object",
                          "properties": {
                            "available": {
                              "type": "boolean"
                            },
                            "reason": {
                              "type": "string",
                              "description": "Present only when available=false."
                            },
                            "howTo": {
                              "type": "string"
                            }
                          }
                        },
                        "testTransaction": {
                          "type": "object",
                          "properties": {
                            "howTo": {
                              "type": "string"
                            }
                          }
                        },
                        "billingIntegration": {
                          "type": "object",
                          "properties": {
                            "howTo": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/entities": {
      "get": {
        "operationId": "listEntities",
        "summary": "List entities",
        "description": "List entities visible to the caller. Entity-scoped keys return only their own entity. Organisation-scoped keys return all entities under the organisation, ordered by default status then creation date — or, if the key is restricted to a subset of entities, only that subset.",
        "tags": [
          "Entities"
        ],
        "responses": {
          "200": {
            "description": "List of entities",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "entities": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Entity"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "operationId": "createEntity",
        "summary": "Create an entity",
        "description": "Create a new entity under the account. Requires an organisation-scoped API key. On success, returns the new entity details and a newly generated entity-scoped API key — this key is returned once only and cannot be retrieved again.",
        "tags": [
          "Entities"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "legalName",
                  "country"
                ],
                "properties": {
                  "legalName": {
                    "type": "string",
                    "description": "Legal name of the entity.",
                    "example": "Acme GmbH"
                  },
                  "country": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 2,
                    "description": "ISO 3166-1 alpha-2 country code of the entity's home jurisdiction.",
                    "example": "DE"
                  },
                  "vatNumber": {
                    "type": "string",
                    "description": "Optional VAT number. Can be added later via PATCH.",
                    "example": "DE123456789"
                  },
                  "extraFields": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    },
                    "description": "Optional country-specific profile fields (e.g. IT regimeFiscale, FR siret, DE steuernummer), validated against the field vocabulary from GET /tax/registrations/field-definitions. Stored alongside vatNumber on the entity's home-country registration.",
                    "example": {
                      "regimeFiscale": "RF01"
                    }
                  },
                  "address": {
                    "type": "object",
                    "description": "Business address, the same nested shape GET returns. Only the keys present are touched; a nested key wins over its flat twin (addressLine1, addressLine2, city, postalCode) when both are sent.",
                    "properties": {
                      "line1": {
                        "type": "string",
                        "nullable": true
                      },
                      "line2": {
                        "type": "string",
                        "nullable": true
                      },
                      "city": {
                        "type": "string",
                        "nullable": true
                      },
                      "postalCode": {
                        "type": "string",
                        "nullable": true
                      }
                    }
                  },
                  "addressLine1": {
                    "type": "string",
                    "description": "Flat alternative to address.line1."
                  },
                  "addressLine2": {
                    "type": "string",
                    "description": "Flat alternative to address.line2."
                  },
                  "city": {
                    "type": "string",
                    "description": "Flat alternative to address.city."
                  },
                  "postalCode": {
                    "type": "string",
                    "description": "Flat alternative to address.postalCode."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Entity created. The apiKey field is returned once only — store it securely.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "entityId": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "accountId": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "name": {
                      "type": "string"
                    },
                    "country": {
                      "type": "string"
                    },
                    "vatNumber": {
                      "type": "string",
                      "nullable": true
                    },
                    "extraFields": {
                      "type": "object",
                      "nullable": true,
                      "additionalProperties": {
                        "type": "string"
                      }
                    },
                    "apiKey": {
                      "type": "string",
                      "description": "Auto-generated entity-scoped API key (csk_live_ or csk_test_ prefix). Returned once only.",
                      "example": "csk_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
                    },
                    "address": {
                      "type": "object",
                      "properties": {
                        "line1": {
                          "type": "string",
                          "nullable": true
                        },
                        "line2": {
                          "type": "string",
                          "nullable": true
                        },
                        "city": {
                          "type": "string",
                          "nullable": true
                        },
                        "postalCode": {
                          "type": "string",
                          "nullable": true
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "vatNumber or an extraFields value fails format/vocabulary validation against the entity's home-country tax registration (Error.code may be VALIDATION_ERROR, INVALID_FORMAT, MISSING_EXTRA_FIELD, or UNKNOWN_EXTRA_FIELD).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-errorCodes": [
              "VALIDATION_ERROR",
              "INVALID_FORMAT",
              "MISSING_EXTRA_FIELD",
              "UNKNOWN_EXTRA_FIELD"
            ]
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Entity-scoped key cannot create entities.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "legalName is missing/empty, country is not a valid ISO 3166-1 alpha-2 code, extraFields is present but not an object, or address is present but not an object. (For a vatNumber/extraFields value that fails format/vocabulary validation, see the 400 response above — that is a distinct, lower-in-the-pipeline check with its own status code.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/entities/{id}": {
      "get": {
        "operationId": "getEntity",
        "summary": "Get an entity",
        "description": "Fetch full details for a single entity. Entity-scoped keys can only access their own entity. Organisation-scoped keys can access any entity in the organisation.",
        "tags": [
          "Entities"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Entity UUID."
          }
        ],
        "responses": {
          "200": {
            "description": "Entity details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Entity"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Entity-scoped key tried to access a different entity. Also returned when an organisation-scoped key's entity allowlist does not include the entity named in the {id} path parameter.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Entity not found or not owned by this account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "operationId": "updateEntity",
        "summary": "Update an entity",
        "description": "Update patchable fields on an entity. An entity-scoped key may only update its own entity; an organisation-scoped key may update any entity in the organisation (subject to its entity allowlist, if restricted).",
        "tags": [
          "Entities"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Legal name of the entity."
                  },
                  "vatNumber": {
                    "type": "string",
                    "nullable": true,
                    "description": "VAT number. Set to null to clear."
                  },
                  "addressLine1": {
                    "type": "string",
                    "nullable": true,
                    "description": "Street address, line 1. Set to an empty string to clear."
                  },
                  "addressLine2": {
                    "type": "string",
                    "nullable": true,
                    "description": "Street address, line 2 (suite, floor, etc.). Set to an empty string to clear."
                  },
                  "city": {
                    "type": "string",
                    "nullable": true,
                    "description": "City. Set to an empty string to clear."
                  },
                  "postalCode": {
                    "type": "string",
                    "nullable": true,
                    "description": "Postal / ZIP code. Set to an empty string to clear."
                  },
                  "confirmNoRegistrations": {
                    "type": "boolean",
                    "description": "Set true to confirm this entity has no tax registrations anywhere yet (e.g. a new or pre-nexus business using only Compliance Radar) — satisfies the add-registration onboarding step without a fabricated registration. Rejected with 422 if the entity already has a real registration. Set false to clear a previous confirmation."
                  },
                  "notifyCustomerByDefault": {
                    "type": "boolean",
                    "description": "Default for the customer invoice notification feature — see notifyCustomer on POST /invoices."
                  },
                  "defaultDeInvoiceFormat": {
                    "type": "string",
                    "nullable": true,
                    "enum": [
                      "ZUGFERD",
                      "XRECHNUNG",
                      "PEPPOL"
                    ],
                    "description": "Entity-level default German invoice format, applied by POST /send when neither the request nor the customer sets countrySpecific.de.invoiceFormat. PEPPOL delivers a Peppol BIS Billing 3.0 invoice over the Peppol network. Null clears the override, falling through to the platform default (ZUGFERD)."
                  },
                  "mxIngestionMode": {
                    "type": "string",
                    "enum": [
                      "sat_pull",
                      "client_push"
                    ],
                    "description": "Mexico only. How this entity receives CFDIs: sat_pull (default) — Clearvo polls SAT's Descarga Masiva service on the entity's behalf, requires an e.firma/CSD credential on file (POST /mx/credentials) or the switch is rejected with 400; client_push — the entity's own AP/ERP system submits CFDI XML directly via POST /mx/inbound/cfdi, no credential needed. Switching sat_pull → client_push does not delete a stored credential."
                  },
                  "mxIngestionStartDate": {
                    "type": "string",
                    "format": "date",
                    "nullable": true,
                    "description": "Mexico only. The earliest CFDI issue date (fecha de emisión) the SAT-pull poller's rolling lookback window will consider for this entity. ISO date (YYYY-MM-DD) or null. Not validated against a minimum/lock here (unlike the dashboard's own Manage view, which surfaces a floor and locks the field once the first Metadata request has been accepted by SAT) — a caller setting this directly should treat it as advisory once polling has started."
                  },
                  "contactName": {
                    "type": "string",
                    "nullable": true,
                    "description": "Seller contact name shown on the invoice itself — invoice CONTENT, required (with contactEmail, contactPhone) for German XRechnung (BR-DE-2/5/6). Set to an empty string to clear."
                  },
                  "contactEmail": {
                    "type": "string",
                    "nullable": true,
                    "description": "Seller contact email shown on the invoice itself. Invoice CONTENT — not where notification-email replies go; see replyToEmail. Set to an empty string to clear."
                  },
                  "contactPhone": {
                    "type": "string",
                    "nullable": true,
                    "description": "Seller contact phone shown on the invoice itself. Set to an empty string to clear."
                  },
                  "replyToEmail": {
                    "type": "string",
                    "nullable": true,
                    "description": "Where a customer's reply to the buyer-invoice-notification email goes. A plain email address, no header-syntax characters (no comma/semicolon/angle-brackets/quote). Deliberately independent of contactEmail. Set to an empty string to clear."
                  },
                  "entityFacts": {
                    "type": "object",
                    "additionalProperties": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "description": "Facts about the entity itself (not any one country registration) — e.g. { \"legalForm\": \"GMBH\" }. A MERGE, per key, same 3-state contract as PATCH /tax/registrations/{id}'s extraFields: a string sets that key, an explicit null deletes it, an omitted key is left unchanged. legalForm gates whether Germany's Handelsregister disclosures (set via PATCH /tax/registrations/{id}'s extraFields) are applicable — set it here, not on a registration, even for a German entity. See GET /entities/fact-definitions for known keys and, for a closed-vocabulary key, its valid values."
                  },
                  "address": {
                    "type": "object",
                    "description": "Business address, the same nested shape GET returns. Only the keys present are touched; a nested key wins over its flat twin (addressLine1, addressLine2, city, postalCode) when both are sent.",
                    "properties": {
                      "line1": {
                        "type": "string",
                        "nullable": true
                      },
                      "line2": {
                        "type": "string",
                        "nullable": true
                      },
                      "city": {
                        "type": "string",
                        "nullable": true
                      },
                      "postalCode": {
                        "type": "string",
                        "nullable": true
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Entity"
                }
              }
            }
          },
          "400": {
            "description": "vatNumber fails format validation for the entity's home country (Error.code=INVALID_FORMAT); mxIngestionMode/mxIngestionStartDate fail their own format checks; mxIngestionMode=sat_pull was sent for an entity with no e.firma on file yet (Error.code=MX_CREDENTIALS_REQUIRED — upload one first via POST /mx/credentials); or defaultDeInvoiceFormat was sent as something other than 'ZUGFERD', 'XRECHNUNG', 'PEPPOL', or null (Error.code=INVALID_DE_INVOICE_FORMAT). The name update and vatNumber update commit atomically — a rejected patch leaves every field unchanged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-errorCodes": [
              "INVALID_FORMAT",
              "MX_CREDENTIALS_REQUIRED",
              "INVALID_DE_INVOICE_FORMAT"
            ]
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Entity-scoped key tried to update a different entity than the one named in the {id} path parameter. Also returned when an organisation-scoped key's entity allowlist does not include that entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Entity not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "name cannot be empty; confirmNoRegistrations=true was sent for an entity that already has at least one registration on file; entityFacts named a key the registry doesn't recognize (Error.code=UNKNOWN_ENTITY_FACT); or entityFacts set a closed-vocabulary key (e.g. legalForm) to a value outside its options list (Error.code=INVALID_ENTITY_FACT_VALUE).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "x-errorCodes": [
              "UNKNOWN_ENTITY_FACT",
              "INVALID_ENTITY_FACT_VALUE"
            ]
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/entities/{id}/logo": {
      "post": {
        "operationId": "uploadEntityLogo",
        "summary": "Upload or replace an entity's logo",
        "description": "Upload (or replace) the entity's own logo, rendered on the buyer-facing invoice-notification email, the hosted invoice-view page, and the downloadable invoice PDF, in place of the platform's own logo. Stored in private storage and served back only through the app-mediated logoUrl on GET/PATCH /entities/{id} — never a raw storage URL. An entity-scoped key may only upload for its own entity; an organisation-scoped key may upload for any entity in the organisation (subject to its entity allowlist, if restricted).",
        "tags": [
          "Entities"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "The logo image. PNG, JPEG, or WEBP only, up to 2 MB, between 32x32 and 2000x2000 pixels. The declared Content-Type must match the file's actual bytes."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Uploaded",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "No file provided, unsupported content type (must be image/png, image/jpeg, or image/webp), file over 2 MB, image dimensions outside 32x32-2000x2000 pixels, or the file's actual bytes do not match its declared Content-Type.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Entity-scoped key tried to update a different entity than the one named in the {id} path parameter. Also returned when an organisation-scoped key's entity allowlist does not include that entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Entity not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Logo storage is not configured",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "removeEntityLogo",
        "summary": "Remove an entity's logo",
        "description": "Removes the entity's uploaded logo. The buyer-facing invoice email, hosted invoice-view page, and downloadable PDF fall back to the platform's own logo afterwards.",
        "tags": [
          "Entities"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Removed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Entity-scoped key tried to update a different entity than the one named in the {id} path parameter. Also returned when an organisation-scoped key's entity allowlist does not include that entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Entity not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/integrations/woocommerce/disconnect": {
      "post": {
        "operationId": "disconnectWooCommerce",
        "summary": "Disconnect the WooCommerce integration",
        "description": "Removes the active WooCommerce connection for the entity. Idempotent — returns ok:true whether or not a connection existed.",
        "tags": [
          "Integrations"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "OAuthBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Disconnected",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key not permitted for this entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/integrations/woocommerce/tax-toggle": {
      "patch": {
        "operationId": "toggleWooCommerceTaxCalc",
        "summary": "Enable or disable checkout tax calculation for WooCommerce",
        "description": "Toggles whether the connected WooCommerce store calls Clearvo for checkout-time tax calculation.",
        "tags": [
          "Integrations"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "OAuthBearer": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "enabled"
                ],
                "properties": {
                  "enabled": {
                    "type": "boolean",
                    "description": "true to enable checkout tax calculation, false to disable."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Toggled",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "enabled": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request body",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key not permitted for this entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No active WooCommerce connection for this entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/integrations/woocommerce/connect": {
      "post": {
        "operationId": "connectWooCommerce",
        "summary": "Connect a WooCommerce store",
        "description": "Used by the official Taxually for WooCommerce plugin immediately after completing the OAuth connect flow, to register the store's own (read-only) WooCommerce REST API credentials so Clearvo can pull the product catalogue for AI tax classification. Not intended for general integration use — this is plugin-internal plumbing, documented here for transparency of the full API surface. Verifies the credentials against the store before persisting them. Required before calling POST /v1/integrations/woocommerce/sync.",
        "tags": [
          "Integrations"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "OAuthBearer": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "siteUrl",
                  "consumerKey",
                  "consumerSecret"
                ],
                "properties": {
                  "siteUrl": {
                    "type": "string",
                    "format": "uri",
                    "description": "The WooCommerce store's public URL. Must resolve to a public address.",
                    "example": "https://shop.example.com"
                  },
                  "consumerKey": {
                    "type": "string",
                    "description": "WooCommerce REST API consumer key (read-only permission)."
                  },
                  "consumerSecret": {
                    "type": "string",
                    "description": "WooCommerce REST API consumer secret.",
                    "writeOnly": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Connected",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request body — missing or malformed siteUrl/consumerKey/consumerSecret",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key / OAuth token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Could not verify the WooCommerce credentials against the given site URL — check the site URL is reachable and the REST API key/secret are correct with read/write scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/integrations/woocommerce/sync": {
      "post": {
        "operationId": "syncWooCommerce",
        "summary": "Trigger a WooCommerce sync",
        "description": "Trigger an on-demand sync of orders and/or products from a previously connected WooCommerce store. Sync runs asynchronously. NOTE: this path was entirely undocumented before this change (no prior `paths` entry existed) — the shape below is a provisional best guess and should be spot-checked against `app/api/einvoicing/v1/integrations/woocommerce/sync/route.ts` once that repo is available alongside this one.",
        "tags": [
          "Integrations"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "OAuthBearer": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "scope": {
                    "type": "string",
                    "enum": [
                      "orders",
                      "products",
                      "all"
                    ],
                    "default": "all",
                    "description": "What to sync from the connected store."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Sync job queued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "syncId": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "QUEUED"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key / OAuth token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No WooCommerce store connected for this entity — call POST /v1/integrations/woocommerce/connect first",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/integrations/coupa/tax-callout": {
      "post": {
        "operationId": "coupaTaxCallout",
        "summary": "Coupa Tax Call Out receiver",
        "description": "Point a Coupa 'Tax Call Out' (Setup > Integrations > Call Outs, configured entirely inside your own Coupa instance — no Clearvo-side registration step) at this URL to make Clearvo the tax engine for your Coupa AP invoices. Authenticate the Call Out with a Clearvo entity-scoped API key presented as a Bearer token, since Coupa's own Call Out config never offers a custom header. Fires on invoice submission; calculates in purchase direction (your Clearvo entity as buyer, the invoice's supplier as the counterparty) and returns per-line tax that your own Coupa admin maps onto the invoice's Tax Line fields via Process Automator. The request body is Coupa's own invoice JSON, not a Clearvo-defined schema — accepted defensively, both hyphenated and underscored Coupa field-naming conventions.",
        "tags": [
          "Integrations"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Coupa's own serialized invoice object — shape is controlled by Coupa, not this API."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Tax calculated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "calculationId": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "totalAmount": {
                      "type": "number"
                    },
                    "totalTax": {
                      "type": "number"
                    },
                    "totalAmountWithTax": {
                      "type": "number"
                    },
                    "degraded": {
                      "type": "boolean"
                    },
                    "lines": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "taxCode": {
                            "type": "string"
                          },
                          "taxRatePercent": {
                            "type": "number"
                          },
                          "taxableAmount": {
                            "type": "number"
                          },
                          "taxAmount": {
                            "type": "number"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "An identical invoice (same idempotency key) is already being processed — Coupa's own Call Out retry will resolve this",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The invoice could not be mapped or calculated — e.g. no usable currency/line items, or an unsupported jurisdiction/category for this purchase",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/integrations/business-central/register": {
      "post": {
        "operationId": "registerBusinessCentral",
        "summary": "Register a Business Central connection",
        "description": "Called once by the Clearvo Business Central AL extension right after a merchant pastes their API key into its \"Clearvo E-Invoicing Setup\" page and selects Clearvo as the E-Document Service integration. Unlike an OAuth-pull integration, there's no credential to verify or store here — the extension already authenticates every real invoice push via the same key straight to POST /v1/send. This route only records the tenant/environment/company identifiers BC itself knows about, so the connection shows up on the Clearvo dashboard.",
        "tags": [
          "Integrations"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "tenantId": {
                    "type": "string",
                    "description": "The merchant's Business Central (Entra ID) tenant identifier."
                  },
                  "environmentName": {
                    "type": "string",
                    "description": "The BC environment name, e.g. \"Production\" or \"Sandbox\"."
                  },
                  "companyId": {
                    "type": "string",
                    "description": "The BC company identifier the extension is installed against."
                  },
                  "companyName": {
                    "type": "string",
                    "nullable": true,
                    "description": "The BC company's display name, if available. AL can send an explicit null for a blank field rather than omitting the key."
                  }
                },
                "required": [
                  "tenantId",
                  "environmentName",
                  "companyId"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Connection registered",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request body — tenantId/environmentName/companyId missing or malformed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Transient storage error — safe to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": false
                    },
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "STORE_FAILED"
                      ]
                    },
                    "retryable": {
                      "type": "boolean",
                      "example": true
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/integrations/business-central/push-result": {
      "post": {
        "operationId": "reportBusinessCentralPushResult",
        "summary": "Report the outcome of a Business Central invoice push",
        "description": "Fire-and-forget callback the Business Central AL extension calls right after each invoice push to POST /v1/send, reporting the outcome for the dashboard's \"last pushed\" status. Best-effort by design — a failed internal write here is logged but never surfaced to the caller, since the actual invoice was already accepted (or rejected) by /v1/send itself, and this callback carries no state any customer-visible flow depends on. Always returns 200 even when the write behind the scenes fails.",
        "tags": [
          "Integrations"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "success": {
                    "type": "boolean",
                    "description": "Whether the invoice push to /v1/send succeeded."
                  },
                  "status": {
                    "type": "string",
                    "description": "The resulting einvoicing_records status, if known."
                  },
                  "country": {
                    "type": "string"
                  },
                  "error": {
                    "type": "string",
                    "description": "Error message, if success is false."
                  }
                },
                "required": [
                  "success"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged — always returned, regardless of whether the internal write succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request body — success missing or not a boolean",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/ar/credentials": {
      "post": {
        "operationId": "setArCredentials",
        "summary": "Register Argentina AFIP credentials",
        "description": "Register or update AFIP electronic invoicing credentials (CUIT, punto de venta, and WSFE certificate) for an entity. Required before submitting invoices to Argentina.",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "cuit": {
                    "type": "string",
                    "description": "11-digit Argentine CUIT, no dashes or spaces.",
                    "example": "20123456789"
                  },
                  "puntoDeVenta": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 9999,
                    "description": "AFIP point-of-sale number."
                  },
                  "certPem": {
                    "type": "string",
                    "description": "PEM-encoded AFIP WSFE certificate."
                  },
                  "keyPem": {
                    "type": "string",
                    "description": "PEM-encoded private key for the certificate."
                  },
                  "condicionIVAIssuer": {
                    "type": "integer",
                    "description": "AFIP IVA condition code for the issuer. Defaults to 1 (Responsable Inscripto)."
                  }
                },
                "required": [
                  "cuit",
                  "puntoDeVenta",
                  "certPem",
                  "keyPem"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Credentials registered",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "cuit": {
                      "type": "string"
                    },
                    "puntoDeVenta": {
                      "type": "integer"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "get": {
        "operationId": "getArCredentials",
        "summary": "Get Argentina AFIP credentials",
        "description": "Retrieve registered AFIP credentials for an entity. The private key is never returned.",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "Registered credentials",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "cuit": {
                      "type": "string"
                    },
                    "puntoDeVenta": {
                      "type": "integer"
                    },
                    "condicionIVAIssuer": {
                      "type": "integer"
                    },
                    "createdAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updatedAt": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No AR credentials found for this entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "deleteArCredentials",
        "summary": "Remove Argentina AFIP credentials",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "Credentials deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No AR credentials found for this entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/it/profile": {
      "put": {
        "operationId": "setItProfile",
        "summary": "Set Italy Regime Fiscale",
        "description": "Set the entity's Italy Regime Fiscale (tax regime code) — a required profile field, not a credential: Clearvo is the accredited SDI intermediary, so there is no per-entity Italy credential to register. Required before submitting invoices to Italy; a send with no Regime Fiscale on file fails. Requires a current Italy tax registration to already exist for this entity (Tax Registrations API) — 422 MISSING_IT_REGISTRATION if none is found.",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "regimeFiscale": {
                    "type": "string",
                    "description": "Regime Fiscale code, e.g. 'RF01'. GET this endpoint to see the full option list.",
                    "example": "RF01"
                  }
                },
                "required": [
                  "regimeFiscale"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Regime Fiscale saved",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ItProfileResponse"
                }
              }
            }
          },
          "400": {
            "description": "regimeFiscale is missing or not a recognized code",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "No current Italy tax registration is on file for this entity (`code` MISSING_IT_REGISTRATION) — register one via the Tax Registrations API first",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "get": {
        "operationId": "getItProfile",
        "summary": "Get Italy Regime Fiscale",
        "description": "Read back the entity's Italy Regime Fiscale, whether it's configured, and the full list of valid codes with labels.",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "Current profile state — always 200, even when nothing is set yet or no Italy registration exists",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ItProfileResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/pl/credentials": {
      "post": {
        "operationId": "setPlCredentials",
        "summary": "Register Poland KSeF credentials",
        "description": "Register or update KSeF (Krajowy System e-Faktur) credentials for an entity: NIP and API token. Required before submitting invoices to Poland or polling the KSeF inbox. Generate the token in the KSeF taxpayer portal (ksef.mf.gov.pl) with \"wysyłka faktur\" and \"dostęp do faktur\" permissions.",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "nip": {
                    "type": "string",
                    "description": "10-digit Polish NIP, no spaces or dashes.",
                    "example": "1234563218"
                  },
                  "token": {
                    "type": "string",
                    "description": "KSeF API token from the taxpayer portal."
                  },
                  "environment": {
                    "type": "string",
                    "enum": [
                      "production",
                      "test"
                    ],
                    "description": "Defaults to \"production\"."
                  }
                },
                "required": [
                  "nip",
                  "token"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Credentials registered",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "nip": {
                      "type": "string"
                    },
                    "environment": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "get": {
        "operationId": "getPlCredentials",
        "summary": "Get Poland KSeF credentials",
        "description": "Retrieve registered KSeF credentials for an entity. The token is masked.",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "Registered credentials",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "nip": {
                      "type": "string"
                    },
                    "environment": {
                      "type": "string"
                    },
                    "tokenMasked": {
                      "type": "string"
                    },
                    "lastPolledAt": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true
                    },
                    "createdAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updatedAt": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No PL credentials found for this entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "deletePlCredentials",
        "summary": "Remove Poland KSeF credentials",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "Credentials deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No PL credentials found for this entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/pt/credentials": {
      "post": {
        "operationId": "setPtCredentials",
        "summary": "Register Portugal AT credentials",
        "description": "Register or update per-entity Portugal (AT, Autoridade Tributária) e-invoicing credentials: the NIF, plus EITHER the document series registered with AT and its ATCUD validation code (invoices; optionally credit notes and debit notes) OR an AT webservice sub-user + password with no series at all — in which case Clearvo registers one series per document type (FT/NC/ND) with AT automatically (ConsultarSeries pre-check to adopt any series AT already holds for that NIF and document type, then RegistarSerie for the rest) and stores the validation codes AT returns. Required before issuing live Portuguese invoices — Portaria 195/2020 requires every invoice to carry ATCUD = validationCode-sequence, and AT issues the validation code to the taxpayer when they register the series (Portal das Finanças → Faturação → Séries, or this automatic path). Supplying one half of the manual invoiceSeries/invoiceValidationCode pair without the other is always a 400, whether or not a sub-user is also present; a manual pair supplied alongside a sub-user is stored as-is and never auto-registered. Editing is safe: a re-save that omits the series while one is already stored KEEPS the stored FT (and NC/ND) series verbatim — never wipes, never re-registers, never re-enters the pre-certification hold — and a re-save that omits the password keeps the stored one for the same subUser; a supplied pair always replaces the stored one. Every response carries the derived block `series` { FT, NC, ND } (each { series, validationCode, source: manual | auto | sandbox } or null), `seriesRegistration` { status: manual | registered | pending_platform_certification | drift | none, message } and `credentialStatus` (ok | setup_needed | pending_platform_certification) — render them verbatim rather than deriving state from the flat fields. The sub-user's own NIF prefix (\"NIF/n\") must equal the entity's NIF — otherwise 400 PT_SUBUSER_NIF_MISMATCH. An AT authentication fault during automatic registration refuses the save with nothing stored: 400 PT_AT_CREDENTIALS_INVALID (sub-user credential wrong/expired/suspended) or 400 PT_SUBUSER_MISSING_PERMISSION (sub-user lacks the WSE series-communication permission); an AT transport failure is 502 PT_AT_TRANSPORT_ERROR. Sandbox sends need none of this — a sandbox save with a sub-user and no series stores AT's own SANDBOX/\"0\" placeholder series and never calls AT. Saving production credentials also regenerates any of the entity's Portuguese invoices held in SETUP_NEEDED for lack of them. Live Portuguese issuing additionally requires the country to be enabled for your account; until it is, POST /v1/send answers a live PT send with 200 and clearanceStatus HELD_AWAITING_AUTHORIZATION (errorCode MANDATE_SUPPORT_NOT_OPERATIONAL; nothing is needed from you, it is released automatically), and a sub-user-only save is accepted (200) with `pendingPlatformCertification: true` and no series stored yet (AT is never called) — repeat the same POST once the platform is certified to trigger the automatic registration.",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "nif": {
                    "type": "string",
                    "description": "9-digit Portuguese NIF, no \"PT\" prefix.",
                    "example": "500000000"
                  },
                  "invoiceSeries": {
                    "type": "string",
                    "description": "Document series registered with AT for invoices (FT). Letters and digits only. Omit (together with invoiceValidationCode, and with subUser + password set) to have Clearvo register FT/NC/ND series with AT automatically.",
                    "example": "A2026"
                  },
                  "invoiceValidationCode": {
                    "type": "string",
                    "description": "ATCUD validation code AT issued for the invoice series when it was registered. Required iff invoiceSeries is set.",
                    "example": "AAJFJ7X5"
                  },
                  "creditNoteSeries": {
                    "type": "string",
                    "description": "Series registered for credit notes (NC). Without one, a live credit note holds SETUP_NEEDED (PT_SERIES_NOT_CONFIGURED) — there is no fallback onto the invoice series. Registered automatically alongside FT on the sub-user path."
                  },
                  "creditNoteValidationCode": {
                    "type": "string",
                    "description": "ATCUD validation code for the credit-note series. Required iff creditNoteSeries is set."
                  },
                  "debitNoteSeries": {
                    "type": "string",
                    "description": "Series registered for debit notes (ND). Without one, a live debit note holds SETUP_NEEDED (PT_SERIES_NOT_CONFIGURED) — there is no fallback onto the invoice series. Registered automatically alongside FT on the sub-user path."
                  },
                  "debitNoteValidationCode": {
                    "type": "string",
                    "description": "ATCUD validation code for the debit-note series. Required iff debitNoteSeries is set."
                  },
                  "subUser": {
                    "type": "string",
                    "description": "AT webservice sub-user in the form NIF/n (e.g. 500000000/1) — its NIF prefix must equal this entity's own `nif` (400 PT_SUBUSER_NIF_MISMATCH otherwise). Used for the AT series-communication (RegistarSerie/ConsultarSeries) and e-Fatura invoice-data communication webservices. With password set and no manual series, triggers automatic AT series registration. On an update, omitting password keeps the stored one for the same subUser.",
                    "example": "500000000/1"
                  },
                  "password": {
                    "type": "string",
                    "format": "password",
                    "description": "Sub-user password. Required iff subUser is set. Encrypted at rest, never returned."
                  }
                },
                "required": [
                  "nif"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Credentials registered",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "nif": {
                      "type": "string"
                    },
                    "invoiceSeries": {
                      "type": "string",
                      "description": "The stored FT series — the one you supplied, or the one AT returned/adopted during automatic registration (Clearvo-generated identifiers look like \"CV<NIF>FT\"). Empty string when pendingPlatformCertification is true (nothing registered yet)."
                    },
                    "invoiceValidationCode": {
                      "type": "string",
                      "description": "Empty string when pendingPlatformCertification is true."
                    },
                    "creditNoteSeries": {
                      "type": "string",
                      "nullable": true
                    },
                    "creditNoteValidationCode": {
                      "type": "string",
                      "nullable": true
                    },
                    "debitNoteSeries": {
                      "type": "string",
                      "nullable": true
                    },
                    "debitNoteValidationCode": {
                      "type": "string",
                      "nullable": true
                    },
                    "subUser": {
                      "type": "string",
                      "nullable": true
                    },
                    "subUserConfigured": {
                      "type": "boolean"
                    },
                    "driftStatus": {
                      "type": "string",
                      "nullable": true,
                      "enum": [
                        "SETUP_NEEDED"
                      ],
                      "description": "Present only when the daily AT ConsultarSeries drift check found that AT's own record for a stored (document type, series) no longer matches the stored validation code — live issuance for that document type is held until you re-save the credentials. Absent otherwise; a fresh save clears it."
                    },
                    "driftDetail": {
                      "type": "array",
                      "nullable": true,
                      "items": {
                        "type": "object",
                        "properties": {
                          "docType": {
                            "type": "string",
                            "enum": [
                              "FT",
                              "NC",
                              "ND"
                            ]
                          },
                          "code": {
                            "type": "string",
                            "enum": [
                              "PT_SERIES_CODE_MISMATCH"
                            ]
                          },
                          "detectedAt": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      },
                      "description": "Per-document-type detail accompanying driftStatus. Absent otherwise."
                    },
                    "series": {
                      "$ref": "#/components/schemas/PtCredentialSeries"
                    },
                    "seriesRegistration": {
                      "$ref": "#/components/schemas/PtSeriesRegistrationPresentation"
                    },
                    "credentialStatus": {
                      "type": "string",
                      "nullable": true,
                      "enum": [
                        "ok",
                        "setup_needed",
                        "pending_platform_certification"
                      ],
                      "description": "Overall derived state: `ok` (a usable FT series is stored and no drift is flagged), `setup_needed` (no series stored and no sub-user to register one, or the daily AT drift check flagged a stored series), `pending_platform_certification` (a sub-user is stored but registration waits on the platform)."
                    },
                    "pendingPlatformCertification": {
                      "type": "boolean",
                      "description": "Present (true) only when a sub-user-only save was accepted while the country is not yet enabled for your account (or its AT webservice connection is not yet on the production tier) — no series has been registered yet and `message` explains. The platform's post-enablement sweep completes the registration automatically; repeating the POST also works."
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed (`details` lists each field error), or automatic AT series registration refused — `code` PT_SUBUSER_NIF_MISMATCH, PT_AT_CREDENTIALS_INVALID or PT_SUBUSER_MISSING_PERMISSION",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "AT's series-communication webservice could not be reached during automatic registration (`code` PT_AT_TRANSPORT_ERROR) — nothing was stored; retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "get": {
        "operationId": "getPtCredentials",
        "summary": "Get Portugal AT credentials",
        "description": "Retrieve registered Portugal (AT) credentials for an entity. The sub-user password is never returned; series identifiers and ATCUD validation codes are (they print on every invoice).",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "Registered credentials",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "nif": {
                      "type": "string"
                    },
                    "invoiceSeries": {
                      "type": "string"
                    },
                    "invoiceValidationCode": {
                      "type": "string"
                    },
                    "creditNoteSeries": {
                      "type": "string",
                      "nullable": true
                    },
                    "creditNoteValidationCode": {
                      "type": "string",
                      "nullable": true
                    },
                    "debitNoteSeries": {
                      "type": "string",
                      "nullable": true
                    },
                    "debitNoteValidationCode": {
                      "type": "string",
                      "nullable": true
                    },
                    "subUser": {
                      "type": "string",
                      "nullable": true
                    },
                    "subUserConfigured": {
                      "type": "boolean"
                    },
                    "driftStatus": {
                      "type": "string",
                      "nullable": true,
                      "enum": [
                        "SETUP_NEEDED"
                      ],
                      "description": "Present only when the daily AT ConsultarSeries drift check found AT's own record for a stored (document type, series) no longer matches the stored validation code — live issuance for that document type is held until the credentials are re-saved. Absent otherwise."
                    },
                    "driftDetail": {
                      "type": "array",
                      "nullable": true,
                      "items": {
                        "type": "object",
                        "properties": {
                          "docType": {
                            "type": "string",
                            "enum": [
                              "FT",
                              "NC",
                              "ND"
                            ]
                          },
                          "code": {
                            "type": "string",
                            "enum": [
                              "PT_SERIES_CODE_MISMATCH"
                            ]
                          },
                          "detectedAt": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      },
                      "description": "Per-document-type detail accompanying driftStatus. Absent otherwise."
                    },
                    "series": {
                      "$ref": "#/components/schemas/PtCredentialSeries"
                    },
                    "seriesRegistration": {
                      "$ref": "#/components/schemas/PtSeriesRegistrationPresentation"
                    },
                    "credentialStatus": {
                      "type": "string",
                      "nullable": true,
                      "enum": [
                        "ok",
                        "setup_needed",
                        "pending_platform_certification"
                      ],
                      "description": "Overall derived state — see POST's response for the meaning of each value."
                    },
                    "createdAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updatedAt": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No PT credentials found for this entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "deletePtCredentials",
        "summary": "Remove Portugal AT credentials",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "Credentials deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No PT credentials found for this entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/fr/credentials": {
      "post": {
        "operationId": "setFrCredentials",
        "summary": "Register the entity's French tax number and read its activation status",
        "description": "Register or update the entity's French VAT number (numéro de TVA intracommunautaire) — required before issuing, sending, or receiving French invoices. Unlike Poland/Portugal/Hungary/Argentina/Greece, France has no secret to store here: this writes the VAT number onto the same STANDARD tax registration the general registrations API already uses, after normalising it (uppercase, strips spaces/dots, prepends FR if missing) and running a deterministic SIREN check-digit + INSEE key check — a mistyped digit is rejected immediately as 400 INVALID_FORMAT rather than surfacing later as an unexplained \"pending\" status.\n\n**Activation status.** POST and GET return an identical body and always answer 200 — even when nothing is registered yet (GET) or while activation is still pending (POST) — because nothing here is a live round-trip to a tax authority or to the platform's own France delivery partner: verification.method is always platform_configuration, reporting only whether the platform's own connection is set up for this tax number, never a real-time authority check. Status is reported per capability — einvoicing (issuing and receiving) and ereporting (periodic sales reporting) — each independently active | pending_activation | sandbox, since the two can be authorised separately; credentialStatus at the top level is the less-advanced of the two, or not_registered on GET when nothing has been saved yet. While a capability is pending, nothing is needed from you unless we contact you — poll GET to see it flip (hourly is plenty; the value only changes on a platform configuration change, never in real time).\n\n**Sandbox.** With a sandbox key every capability reports sandbox, masking the real production activation state either way; to see production activation, call GET with a live read-scope key. Sandbox sends are accepted locally; inbound receiving cannot be exercised in sandbox.\n\n**Deleting and re-registering.** No DELETE: removing a French VAT number means deregistering the tax registration itself — use DELETE /v1/tax/registrations/{id} instead. Registering the same French tax number on more than one entity is accepted; inbound documents for that number appear in each entity's inbox.\n\n**Onboarding a new French client (three calls).** (1) create the entity with an account-scoped key, (2) POST /v1/fr/credentials with X-Entity-Id to register its VAT number, (3) PATCH /v1/tax/reporting-obligations to enable fr_ereporting if the entity has a French e-reporting obligation (effectiveFrom and vatRegime required; refused with REGISTRATION_REQUIRED if step 2 was skipped) — see nextSteps[].applicableTo, since this endpoint cannot determine obligation-applicability on the entity's behalf. Inbound receiving starts automatically once einvoicing shows active — no separate step needed.\n\nOne caveat this endpoint cannot see or report on: einvoicing's receiving half also depends on this VAT number's SIREN being routed to the platform in France's own central directory (the Annuaire) — that routing fact is outside what this status reflects.",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "taxNumber": {
                    "type": "string",
                    "description": "French VAT number. Accepts FR-prefixed, lowercase, spaced/punctuated, or the bare 11-character SIREN+key form — normalised and canonicalised before storing. Rejected as 400 INVALID_FORMAT if the shape doesn't match, the SIREN fails its check digit, or a numeric key doesn't match its SIREN.",
                    "example": "FR12000000000"
                  }
                },
                "required": [
                  "taxNumber"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Credentials registered — always 200; read credentialStatus/capabilities for the actual activation state.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "sandbox": {
                      "type": "boolean",
                      "description": "True when this write ran against the sandbox environment — matches the API key used, not a body field. Sandbox always reads every capability (and credentialStatus) as sandbox, regardless of platform configuration."
                    },
                    "taxNumber": {
                      "type": "string"
                    },
                    "siret": {
                      "type": "string",
                      "nullable": true,
                      "description": "Read-through of the entity's own legalRegistrationNumber fact (PATCH /v1/entities/{id}'s entityFacts) — entity-level, not scoped to this registration. Not requestable here."
                    },
                    "previousTaxNumber": {
                      "type": "string",
                      "description": "Present only when this POST changed an already-stored tax number — the value it replaced."
                    },
                    "credentialStatus": {
                      "type": "string",
                      "enum": [
                        "active",
                        "pending_activation",
                        "sandbox",
                        "not_registered"
                      ],
                      "description": "The less-advanced of the two capabilities below — read capabilities for the one that applies to you."
                    },
                    "capabilities": {
                      "type": "object",
                      "properties": {
                        "einvoicing": {
                          "$ref": "#/components/schemas/FrCapabilityPresentation"
                        },
                        "ereporting": {
                          "$ref": "#/components/schemas/FrCapabilityPresentation"
                        }
                      }
                    },
                    "verification": {
                      "type": "object",
                      "description": "States what kind of check this is, and when — never a claim that the tax authority or the platform's France delivery partner was actually asked.",
                      "properties": {
                        "method": {
                          "type": "string",
                          "enum": [
                            "platform_configuration"
                          ]
                        },
                        "checkedAt": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    },
                    "siren": {
                      "type": "string",
                      "nullable": true,
                      "description": "The last 9 digits of taxNumber, derived read-only — never a separate stored or requested field."
                    },
                    "nextSteps": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/FrNextStep"
                      },
                      "description": "Static follow-on actions this response documents, never performed automatically — today only ever the e-reporting PATCH, shown to every caller — seller or buyer — since this endpoint has no way to know whether the registering entity has an e-reporting obligation; read nextSteps[].applicableTo before assuming it applies. Inbound receiving starts automatically once einvoicing shows active — no separate step needed."
                    },
                    "message": {
                      "type": "string",
                      "description": "One plain-language sentence summarising both capabilities."
                    },
                    "createdAt": {
                      "type": "string",
                      "format": "date-time",
                      "description": "When this entity's French tax registration was first created."
                    },
                    "updatedAt": {
                      "type": "string",
                      "format": "date-time",
                      "description": "When this entity's French tax registration was last written — this POST, if it changed anything."
                    }
                  }
                },
                "examples": {
                  "pending_activation": {
                    "summary": "Registered, nothing granted yet",
                    "value": {
                      "ok": true,
                      "entityId": "ent_01H8X9K2M4QRT7VWYZ3ABCDEF",
                      "sandbox": false,
                      "taxNumber": "FR12000000000",
                      "siret": null,
                      "credentialStatus": "pending_activation",
                      "capabilities": {
                        "einvoicing": {
                          "status": "pending_activation",
                          "label": "Sending and receiving invoices",
                          "message": "Sending and receiving invoices for FR12000000000: pending activation — nothing needed from you unless we contact you.",
                          "actionOwner": "platform"
                        },
                        "ereporting": {
                          "status": "pending_activation",
                          "label": "Reporting sales to the tax office",
                          "message": "Reporting sales to the tax office for FR12000000000: pending activation — nothing needed from you unless we contact you.",
                          "actionOwner": "platform"
                        }
                      },
                      "verification": {
                        "method": "platform_configuration",
                        "checkedAt": "2026-09-22T10:00:00Z"
                      },
                      "siren": "000000000",
                      "nextSteps": [
                        {
                          "action": "enable_ereporting",
                          "method": "PATCH",
                          "path": "/v1/tax/reporting-obligations",
                          "body": {
                            "obligations": {
                              "fr_ereporting": {
                                "enabled": true,
                                "effectiveFrom": null,
                                "vatRegime": null
                              }
                            }
                          },
                          "applicableTo": "entities with a French e-reporting obligation — typically sellers with French B2C or cross-border B2B sales, and possibly established buyers purchasing from suppliers outside France; confirm the entity's own scope",
                          "requiredInputs": [
                            {
                              "field": "effectiveFrom",
                              "description": "The date this entity's French e-reporting obligation applies from — set by the French rollout calendar, not a free preference."
                            },
                            {
                              "field": "vatRegime",
                              "description": "This entity's own real French VAT regime."
                            }
                          ]
                        }
                      ],
                      "message": "French e-invoicing and e-reporting for FR12000000000: pending activation — nothing needed from you unless we contact you.",
                      "createdAt": "2026-09-22T10:00:00Z",
                      "updatedAt": "2026-09-22T10:00:00Z"
                    }
                  },
                  "active": {
                    "summary": "Both capabilities live",
                    "value": {
                      "ok": true,
                      "entityId": "ent_01H8X9K2M4QRT7VWYZ3ABCDEF",
                      "sandbox": false,
                      "taxNumber": "FR12000000000",
                      "siret": null,
                      "credentialStatus": "active",
                      "capabilities": {
                        "einvoicing": {
                          "status": "active",
                          "label": "Sending and receiving invoices",
                          "message": "Sending and receiving invoices for FR12000000000: live. Receiving also depends on the buyer's own routing to us in the French central directory, which this status cannot see.",
                          "actionOwner": null
                        },
                        "ereporting": {
                          "status": "active",
                          "label": "Reporting sales to the tax office",
                          "message": "Reporting sales to the tax office for FR12000000000: live.",
                          "actionOwner": null
                        }
                      },
                      "verification": {
                        "method": "platform_configuration",
                        "checkedAt": "2026-09-22T10:00:00Z"
                      },
                      "siren": "000000000",
                      "nextSteps": [
                        {
                          "action": "enable_ereporting",
                          "method": "PATCH",
                          "path": "/v1/tax/reporting-obligations",
                          "body": {
                            "obligations": {
                              "fr_ereporting": {
                                "enabled": true,
                                "effectiveFrom": null,
                                "vatRegime": null
                              }
                            }
                          },
                          "applicableTo": "entities with a French e-reporting obligation — typically sellers with French B2C or cross-border B2B sales, and possibly established buyers purchasing from suppliers outside France; confirm the entity's own scope",
                          "requiredInputs": [
                            {
                              "field": "effectiveFrom",
                              "description": "The date this entity's French e-reporting obligation applies from — set by the French rollout calendar, not a free preference."
                            },
                            {
                              "field": "vatRegime",
                              "description": "This entity's own real French VAT regime."
                            }
                          ]
                        }
                      ],
                      "message": "French e-invoicing and e-reporting for FR12000000000: live. Receiving also depends on the buyer's own routing to us in the French central directory, which this status cannot see.",
                      "createdAt": "2026-09-22T10:00:00Z",
                      "updatedAt": "2026-09-22T10:00:00Z"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed (`details` lists each field error), or the tax number failed its integrity check — `code` INVALID_FORMAT",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "getFrCredentials",
        "summary": "Get France platform credentials",
        "description": "Read back the entity's registered French VAT number and its per-capability activation status — the same body POST returns. Always 200, even when the entity has no French tax registration at all (credentialStatus: not_registered) — shape-stable so a poller never has to special-case \"nothing saved yet\". Poll this (hourly is plenty) rather than POST-ing repeatedly to check activation; the value only changes on a platform configuration change, never in real time. With a sandbox key every capability reports sandbox, masking the real production activation state either way; to see production activation, call this endpoint with a live read-scope key. Sandbox sends are accepted locally; inbound receiving cannot be exercised in sandbox.",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "Current status — always 200, including not_registered.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "sandbox": {
                      "type": "boolean",
                      "description": "True when this read ran against the sandbox environment — matches the API key used."
                    },
                    "taxNumber": {
                      "type": "string",
                      "nullable": true
                    },
                    "siret": {
                      "type": "string",
                      "nullable": true
                    },
                    "credentialStatus": {
                      "type": "string",
                      "enum": [
                        "active",
                        "pending_activation",
                        "sandbox",
                        "not_registered"
                      ],
                      "description": "The less-advanced of the two capabilities below — read capabilities for the one that applies to you."
                    },
                    "capabilities": {
                      "type": "object",
                      "properties": {
                        "einvoicing": {
                          "$ref": "#/components/schemas/FrCapabilityPresentation"
                        },
                        "ereporting": {
                          "$ref": "#/components/schemas/FrCapabilityPresentation"
                        }
                      }
                    },
                    "verification": {
                      "type": "object",
                      "properties": {
                        "method": {
                          "type": "string",
                          "enum": [
                            "platform_configuration"
                          ]
                        },
                        "checkedAt": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    },
                    "siren": {
                      "type": "string",
                      "nullable": true
                    },
                    "nextSteps": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/FrNextStep"
                      }
                    },
                    "message": {
                      "type": "string"
                    },
                    "createdAt": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true,
                      "description": "Null when the entity has no French tax registration on file yet."
                    },
                    "updatedAt": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true,
                      "description": "Null when the entity has no French tax registration on file yet."
                    }
                  }
                },
                "examples": {
                  "pending_activation": {
                    "summary": "Registered, nothing granted yet",
                    "value": {
                      "ok": true,
                      "entityId": "ent_01H8X9K2M4QRT7VWYZ3ABCDEF",
                      "sandbox": false,
                      "taxNumber": "FR12000000000",
                      "siret": null,
                      "credentialStatus": "pending_activation",
                      "capabilities": {
                        "einvoicing": {
                          "status": "pending_activation",
                          "label": "Sending and receiving invoices",
                          "message": "Sending and receiving invoices for FR12000000000: pending activation — nothing needed from you unless we contact you.",
                          "actionOwner": "platform"
                        },
                        "ereporting": {
                          "status": "pending_activation",
                          "label": "Reporting sales to the tax office",
                          "message": "Reporting sales to the tax office for FR12000000000: pending activation — nothing needed from you unless we contact you.",
                          "actionOwner": "platform"
                        }
                      },
                      "verification": {
                        "method": "platform_configuration",
                        "checkedAt": "2026-09-22T10:00:00Z"
                      },
                      "siren": "000000000",
                      "nextSteps": [
                        {
                          "action": "enable_ereporting",
                          "method": "PATCH",
                          "path": "/v1/tax/reporting-obligations",
                          "body": {
                            "obligations": {
                              "fr_ereporting": {
                                "enabled": true,
                                "effectiveFrom": null,
                                "vatRegime": null
                              }
                            }
                          },
                          "applicableTo": "entities with a French e-reporting obligation — typically sellers with French B2C or cross-border B2B sales, and possibly established buyers purchasing from suppliers outside France; confirm the entity's own scope",
                          "requiredInputs": [
                            {
                              "field": "effectiveFrom",
                              "description": "The date this entity's French e-reporting obligation applies from — set by the French rollout calendar, not a free preference."
                            },
                            {
                              "field": "vatRegime",
                              "description": "This entity's own real French VAT regime."
                            }
                          ]
                        }
                      ],
                      "message": "French e-invoicing and e-reporting for FR12000000000: pending activation — nothing needed from you unless we contact you.",
                      "createdAt": "2026-09-22T10:00:00Z",
                      "updatedAt": "2026-09-22T10:00:00Z"
                    }
                  },
                  "active": {
                    "summary": "Both capabilities live",
                    "value": {
                      "ok": true,
                      "entityId": "ent_01H8X9K2M4QRT7VWYZ3ABCDEF",
                      "sandbox": false,
                      "taxNumber": "FR12000000000",
                      "siret": null,
                      "credentialStatus": "active",
                      "capabilities": {
                        "einvoicing": {
                          "status": "active",
                          "label": "Sending and receiving invoices",
                          "message": "Sending and receiving invoices for FR12000000000: live. Receiving also depends on the buyer's own routing to us in the French central directory, which this status cannot see.",
                          "actionOwner": null
                        },
                        "ereporting": {
                          "status": "active",
                          "label": "Reporting sales to the tax office",
                          "message": "Reporting sales to the tax office for FR12000000000: live.",
                          "actionOwner": null
                        }
                      },
                      "verification": {
                        "method": "platform_configuration",
                        "checkedAt": "2026-09-22T10:00:00Z"
                      },
                      "siren": "000000000",
                      "nextSteps": [
                        {
                          "action": "enable_ereporting",
                          "method": "PATCH",
                          "path": "/v1/tax/reporting-obligations",
                          "body": {
                            "obligations": {
                              "fr_ereporting": {
                                "enabled": true,
                                "effectiveFrom": null,
                                "vatRegime": null
                              }
                            }
                          },
                          "applicableTo": "entities with a French e-reporting obligation — typically sellers with French B2C or cross-border B2B sales, and possibly established buyers purchasing from suppliers outside France; confirm the entity's own scope",
                          "requiredInputs": [
                            {
                              "field": "effectiveFrom",
                              "description": "The date this entity's French e-reporting obligation applies from — set by the French rollout calendar, not a free preference."
                            },
                            {
                              "field": "vatRegime",
                              "description": "This entity's own real French VAT regime."
                            }
                          ]
                        }
                      ],
                      "message": "French e-invoicing and e-reporting for FR12000000000: live. Receiving also depends on the buyer's own routing to us in the French central directory, which this status cannot see.",
                      "createdAt": "2026-09-22T10:00:00Z",
                      "updatedAt": "2026-09-22T10:00:00Z"
                    }
                  },
                  "not_registered": {
                    "summary": "No French tax registration on file",
                    "value": {
                      "ok": true,
                      "entityId": "ent_01H8X9K2M4QRT7VWYZ3ABCDEF",
                      "sandbox": false,
                      "taxNumber": null,
                      "siret": null,
                      "credentialStatus": "not_registered",
                      "capabilities": {
                        "einvoicing": {
                          "status": "pending_activation",
                          "label": "Sending and receiving invoices",
                          "message": "Sending and receiving invoices: no French tax number is registered yet.",
                          "actionOwner": null
                        },
                        "ereporting": {
                          "status": "pending_activation",
                          "label": "Reporting sales to the tax office",
                          "message": "Reporting sales to the tax office: no French tax number is registered yet.",
                          "actionOwner": null
                        }
                      },
                      "verification": {
                        "method": "platform_configuration",
                        "checkedAt": "2026-09-22T10:00:00Z"
                      },
                      "siren": null,
                      "nextSteps": [
                        {
                          "action": "register",
                          "method": "POST",
                          "path": "/v1/fr/credentials",
                          "body": {
                            "taxNumber": "FR12000000000"
                          },
                          "applicableTo": "sellers and buyers"
                        }
                      ],
                      "message": "No French tax number is registered for this entity yet. Register one via POST /v1/fr/credentials to set up French e-invoicing on this platform.",
                      "createdAt": null,
                      "updatedAt": null
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/pt/saft": {
      "get": {
        "operationId": "getPtMonthlySaft",
        "summary": "Download the Portugal monthly SAF-T (PT) billing file",
        "description": "Portugal AT SAF-T (PT) 1.04_01 monthly billing file — every FT/NC/ND document Clearvo issued for this entity in the given calendar month, in one AuditFile XML per Portaria 302/2016, regardless of each document's e-Fatura communication status (the monthly file is a complete-period artefact by definition). Rebuilt from the stored, already-issued documents on every call (never re-signed or re-numbered) and checked against the 1.04_01 XSD before it is returned (see xsdValidated/xsdValid on the summary). The default response is the XML itself as a download (Content-Type application/xml, Content-Disposition attachment, filename `SAFT-PT_<NIF>_<YYYY-MM>.xml`); pass `format=summary` for a JSON manifest instead — per-document list, sha256 of the file, XSD outcome — for a caller that wants the coverage picture without downloading the file; pass `format=status` for the CHEAP counts-only view (no file build, no XSD): documentCount / reportedToAt / notReportedToAt / dueDate (the 5th of the following month) / attention + ready-to-render summaryLabel and attentionMessage, plus defaultPeriod (the previous calendar month in Europe/Lisbon) — the shape to poll for \"is this month done, what is still not reported to AT, when is it due\". `format=status` answers 200 with `applicable: false` and zero counts for an entity with no Portuguese registration, where the file itself must refuse 400 PT_NIF_NOT_REGISTERED. A month with no PT documents at all is a valid, empty file (NumberOfEntries 0) — never a 404. A live (non-sandbox) file is refused 422 PT_PLATFORM_IDENTITY_NOT_CONFIGURED while the platform's own software-producer identity is still being provisioned (nothing is needed from you; status still works), and 422 PT_SAFT_TOO_LARGE when the month holds more than 10,000 documents (`limit` in the body; contact support for an out-of-band file). Generation is refused with 409 in two cases so a file AT would flag is never silently emitted: PT_SAFT_SERIES_GAP — an unexplained numbering gap in a document series used this month (checked against the entity's full history for that series, not just the month); PT_SAFT_NEEDS_INFO — one or more documents need more information before the file can be built (a non-EUR document, a document issued before its hash/ATCUD data was persisted, a fiscal-region/VAT-rate mismatch, or a zero-rated line with no resolvable VAT exemption code). The body's `gaps` / `issues` name exactly what to fix. The MCP tool `get_pt_monthly_saft` is this endpoint's summary view.",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "period",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}$"
            },
            "description": "Calendar month, as YYYY-MM.",
            "example": "2026-06"
          },
          {
            "name": "format",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "summary",
                "status"
              ]
            },
            "description": "Omit for the XML file itself; `summary` returns the JSON manifest (builds the whole file); `status` returns the cheap counts/attention view (no file build)."
          }
        ],
        "responses": {
          "200": {
            "description": "The SAF-T (PT) XML file (default), the JSON manifest when format=summary, or the counts/attention view when format=status",
            "headers": {
              "Content-Disposition": {
                "schema": {
                  "type": "string"
                },
                "description": "XML response only — `attachment; filename=\"SAFT-PT_<NIF>_<YYYY-MM>.xml\"`."
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-RateLimit-Scope": {
                "$ref": "#/components/headers/X-RateLimit-Scope"
              }
            },
            "content": {
              "application/xml": {
                "schema": {
                  "type": "string",
                  "format": "binary",
                  "description": "The AuditFile XML (SAF-T PT 1.04_01), UTF-8."
                }
              },
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "title": "PtSaftStatus",
                      "type": "object",
                      "description": "format=status — counts only. Every ACCEPTED FT/NC/ND issued in the month counts as a document that must reach AT by dueDate; \"reported to AT\" means its real-time pt_efatura communication was ACCEPTED, everything else (pending/rejected, or a PT record issued without pt_efatura enabled — for which the monthly file IS the communication channel) counts as not reported. All calendar arithmetic is in Europe/Lisbon.",
                      "properties": {
                        "ok": {
                          "type": "boolean",
                          "example": true
                        },
                        "applicable": {
                          "type": "boolean",
                          "description": "False when the entity has no Portuguese tax registration — counts are 0, nothing is due, no 400."
                        },
                        "period": {
                          "type": "string",
                          "example": "2026-08"
                        },
                        "defaultPeriod": {
                          "type": "string",
                          "description": "The previous calendar month in Europe/Lisbon, YYYY-MM — the month a caller most likely wants.",
                          "example": "2026-08"
                        },
                        "documentCount": {
                          "type": "integer"
                        },
                        "reportedToAt": {
                          "type": "integer"
                        },
                        "notReportedToAt": {
                          "type": "integer"
                        },
                        "dueDate": {
                          "type": "string",
                          "format": "date",
                          "description": "The 5th of the month after `period`.",
                          "example": "2026-09-05"
                        },
                        "attention": {
                          "type": "boolean",
                          "description": "True only when notReportedToAt > 0 AND today (Europe/Lisbon) is on/after the 1st of the month after `period` — inside the reporting window or overdue. Never true mid-period."
                        },
                        "attentionMessage": {
                          "type": "string",
                          "nullable": true,
                          "description": "Ready-to-render attention line; null when attention is false.",
                          "example": "2 Portuguese invoices from August are not yet reported to AT — download the monthly file (due 5 September)."
                        },
                        "summaryLabel": {
                          "type": "string",
                          "description": "Ready-to-render one-liner.",
                          "example": "August 2026 · 12 documents · 10 reported to AT · 2 not reported · due 5 September"
                        }
                      }
                    },
                    {
                      "title": "PtSaftSummary",
                      "type": "object",
                      "description": "format=summary — the file is fully built, hashed and XSD-checked.",
                      "properties": {
                        "ok": {
                          "type": "boolean",
                          "example": true
                        },
                        "period": {
                          "type": "string",
                          "example": "2026-06"
                        },
                        "nif": {
                          "type": "string",
                          "description": "The entity's registered PT NIF (the file's TaxRegistrationNumber).",
                          "example": "500000000"
                        },
                        "numberOfEntries": {
                          "type": "integer",
                          "description": "Documents in the file (SalesInvoices/NumberOfEntries). 0 for a month with no PT documents."
                        },
                        "sha256": {
                          "type": "string",
                          "description": "Hex SHA-256 of the exact XML bytes the default (XML) response would return for this period."
                        },
                        "manifest": {
                          "type": "array",
                          "description": "One entry per document in the file, in file order.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "recordId": {
                                "type": "string",
                                "description": "Clearvo's own id for the document — fetch it via GET /v1/invoices/{id}."
                              },
                              "invoiceNo": {
                                "type": "string",
                                "description": "The SAF-T InvoiceNo (e.g. \"FT A2026/12\").",
                                "example": "FT A2026/12"
                              },
                              "seriesType": {
                                "type": "string",
                                "enum": [
                                  "FT",
                                  "NC",
                                  "ND"
                                ]
                              },
                              "seqNum": {
                                "type": "integer",
                                "description": "Sequential number within the series."
                              },
                              "issueDate": {
                                "type": "string",
                                "format": "date"
                              },
                              "grossTotal": {
                                "type": "number"
                              }
                            }
                          }
                        },
                        "xsdValidated": {
                          "type": "boolean",
                          "description": "Whether the XSD validation step actually ran on this deployment."
                        },
                        "xsdValid": {
                          "type": "boolean",
                          "nullable": true,
                          "description": "The XSD outcome; null when xsdValidated is false."
                        },
                        "filename": {
                          "type": "string",
                          "description": "The filename the XML download would carry.",
                          "example": "SAFT-PT_500000000_2026-06.xml"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "`code` PT_SAFT_PERIOD_REQUIRED (period missing), PT_SAFT_INVALID_PERIOD (not YYYY-MM), or — XML/summary only — PT_NIF_NOT_REGISTERED (the entity has no Portuguese tax registration — register a PT NIF via POST /v1/pt/credentials first; format=status answers 200 applicable:false instead)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "XML/summary only — the file cannot be produced: `code` PT_PLATFORM_IDENTITY_NOT_CONFIGURED (live only: the platform's own software-producer identity is still being provisioned; nothing needed from you, the download opens automatically once done; sandbox files are unaffected) or PT_SAFT_TOO_LARGE (the month holds more than `limit` documents — contact support for an out-of-band file). format=status still works in both cases.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": false
                    },
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "PT_PLATFORM_IDENTITY_NOT_CONFIGURED",
                        "PT_SAFT_TOO_LARGE"
                      ]
                    },
                    "hint": {
                      "type": "string"
                    },
                    "limit": {
                      "type": "integer",
                      "description": "PT_SAFT_TOO_LARGE only — the per-file document cap (10000 today)."
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Generation refused — `code` PT_SAFT_SERIES_GAP (with `gaps`) or PT_SAFT_NEEDS_INFO (with `issues`)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": false
                    },
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "PT_SAFT_SERIES_GAP",
                        "PT_SAFT_NEEDS_INFO"
                      ]
                    },
                    "gaps": {
                      "type": "array",
                      "description": "PT_SAFT_SERIES_GAP only — one entry per affected series.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "seriesType": {
                            "type": "string",
                            "enum": [
                              "FT",
                              "NC",
                              "ND"
                            ]
                          },
                          "series": {
                            "type": "string"
                          },
                          "missingSeqNums": {
                            "type": "array",
                            "items": {
                              "type": "integer"
                            }
                          }
                        }
                      }
                    },
                    "issues": {
                      "type": "array",
                      "description": "PT_SAFT_NEEDS_INFO only — every affected document, collected in one pass.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "recordId": {
                            "type": "string"
                          },
                          "invoiceNo": {
                            "type": "string"
                          },
                          "field": {
                            "type": "string"
                          },
                          "message": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "The file could not be generated right now — retry shortly",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/fr/inbound/poll": {
      "post": {
        "operationId": "pollFrInbox",
        "summary": "Poll France inbound (customer) invoice inbox and resolve outbound status",
        "description": "France mechanism — invoices are delivered through an accredited third-party platform. Unlike Poland, this endpoint does double duty: it (1) resolves clearance/business status on your own PENDING outbound submissions and (2) fetches new invoices received from suppliers. Register the entity's French VAT number first via POST /v1/fr/credentials — this endpoint then polls automatically for any French registration, but new inbound documents only actually appear once the einvoicing capability shows active (poll GET /v1/fr/credentials to watch it flip) and the VAT number's SIREN is routed in France's own central directory (the Annuaire). Automatically driven every 5 minutes in the background regardless of whether you call this yourself; call it manually only if you need a result sooner than the next scheduled sweep. Each newly received invoice is stored with direction=\"inbound\" (list via GET /invoices?direction=inbound, or fetch individually via GET /invoices/{id}) and fires an invoice.received webhook. Requires an API key with write access (polling ingests invoices into your inbox); a read-only key is refused with 403.",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "Poll completed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "entityId": {
                            "type": "string"
                          },
                          "resolved": {
                            "type": "integer",
                            "description": "Count of your own outbound submissions whose status was resolved this call."
                          },
                          "newInvoices": {
                            "type": "integer",
                            "description": "Count of newly received invoices stored this call."
                          },
                          "errors": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    },
                    "totalNewInvoices": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "France e-invoicing is not configured on this deployment",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/pl/inbound/poll": {
      "post": {
        "operationId": "pollPlInbox",
        "summary": "Poll Poland KSeF inbound (customer) invoice inbox",
        "description": "Fetch new invoices received from suppliers via Poland KSeF. KSeF has no push mechanism — an invoice a supplier issues to your NIP only becomes visible to you once this endpoint is called, so poll it on whatever schedule fits your integration (there is no automatic background schedule run on your behalf today). Requires set_pl_credentials to have been called first with a token that carries the \"dostęp do faktur\" (invoice access) permission — a token registered only for sending (\"wysyłka faktur\") will fail. Each newly received invoice is stored with direction=\"inbound\" (list via GET /invoices?direction=inbound, or fetch individually via GET /invoices/{id}), runs a Biała Lista (White List) check on the seller's bank account IBAN, and fires an invoice.received webhook. Requires an API key with write access (polling ingests invoices into your inbox); a read-only key is refused with 403.",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "Poll completed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "newInvoices": {
                      "type": "integer",
                      "description": "Count of newly received invoices stored this call."
                    },
                    "errors": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Non-fatal per-invoice errors, if any (e.g. a single invoice's XML failed to download). A poll can partially succeed."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "No KSeF credentials configured, or credentials found but no Polish NIP registered for this entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/test-runs": {
      "post": {
        "operationId": "startTestRun",
        "summary": "Start a run of your integration tests",
        "description": "Starts a run of your integration tests in the sandbox and returns at once with a runId; poll GET /v1/test-runs/{id} until status is no longer queued or running. For CI: call this, poll the run, fail the build when status is failed. By default it runs the tests the platform can check on its own (tag automatic). Only one run can be in progress at a time (409 RUN_IN_PROGRESS). Retrying with the same x-idempotency-key returns the same run with 200 and idempotent: true. Heavy rate class. These results show how your integration handled simulated scenarios in the sandbox; nothing is sent to any tax authority, network or supplier, and this is not a certification. Sandbox key only: a live key, an internal key or a partner proxy key gets 403 (SANDBOX_ONLY or KEY_NOT_ALLOWED). An entity key sees only its own entity; an organisation key sees its entities (send x-entity-id to name one).",
        "tags": [
          "Integration Tests"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TestRunStartRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "The run started.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TestRunStarted"
                }
              }
            }
          },
          "200": {
            "description": "The same x-idempotency-key already started this run (idempotent: true).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TestRunStarted"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body (INVALID_REQUEST, with the offending field).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Not a sandbox key (SANDBOX_ONLY) or a key kind that cannot use tests (KEY_NOT_ALLOWED).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A run is already in progress (RUN_IN_PROGRESS).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "get": {
        "operationId": "listTestRuns",
        "summary": "List your test runs",
        "description": "Your recent test runs, newest first, up to 50, each with its status and counts. Sandbox key only: a live key, an internal key or a partner proxy key gets 403 (SANDBOX_ONLY or KEY_NOT_ALLOWED). An entity key sees only its own entity; an organisation key sees its entities (send x-entity-id to name one).",
        "tags": [
          "Integration Tests"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "The runs.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TestRunList"
                }
              }
            }
          },
          "403": {
            "description": "Not a sandbox key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/test-runs/{id}": {
      "get": {
        "operationId": "getTestRun",
        "summary": "Get a test run",
        "description": "One run: its status (queued, running, waiting, passed, failed or cancelled), the counts, and each test's outcome with the ids behind it. status is passed only when every required test passed with the platform's own check; a test marked done by hand never counts. Poll until status is not queued or running. Sandbox key only: a live key, an internal key or a partner proxy key gets 403 (SANDBOX_ONLY or KEY_NOT_ALLOWED). An entity key sees only its own entity; an organisation key sees its entities (send x-entity-id to name one).",
        "tags": [
          "Integration Tests"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The runId from POST /v1/test-runs or GET /v1/test-runs."
          },
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "The run.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TestRun"
                }
              }
            }
          },
          "403": {
            "description": "Not a sandbox key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such run for this key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/test-cases": {
      "get": {
        "operationId": "listTestCases",
        "summary": "List your tests",
        "description": "The tests you can run: the standard ones and the ones added for your account or by you, each with whether it applies to your setup, whether it is checked automatically or marked done by hand, its steps and the expected outcome. Sandbox key only: a live key, an internal key or a partner proxy key gets 403 (SANDBOX_ONLY or KEY_NOT_ALLOWED). An entity key sees only its own entity; an organisation key sees its entities (send x-entity-id to name one).",
        "tags": [
          "Integration Tests"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "The tests.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TestCaseList"
                }
              }
            }
          },
          "403": {
            "description": "Not a sandbox key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "operationId": "createTestCase",
        "summary": "Add a test of your own",
        "description": "Adds a test of your own to your suite. With a check the platform verifies it automatically in a run; without one you mark it done by hand in the dashboard. Use GET /v1/test-cases/catalogue for the checks and choices a test accepts. Limits are in the catalogue (409 CASE_LIMIT_REACHED). Sandbox key only: a live key, an internal key or a partner proxy key gets 403 (SANDBOX_ONLY or KEY_NOT_ALLOWED). An entity key sees only its own entity; an organisation key sees its entities (send x-entity-id to name one).",
        "tags": [
          "Integration Tests"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TestCaseRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The test was added.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TestCaseWritten"
                }
              }
            }
          },
          "400": {
            "description": "A field is invalid (INVALID_CASE, with the field).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Not a sandbox key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "You have reached the number of tests you can add (CASE_LIMIT_REACHED).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/test-cases/catalogue": {
      "get": {
        "operationId": "getTestCaseCatalogue",
        "summary": "Get the choices for adding a test",
        "description": "The closed choices for adding a test: the checks and their settings, who a test can apply to, and the limits. Sandbox key only: a live key, an internal key or a partner proxy key gets 403 (SANDBOX_ONLY or KEY_NOT_ALLOWED). An entity key sees only its own entity; an organisation key sees its entities (send x-entity-id to name one).",
        "tags": [
          "Integration Tests"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "The catalogue.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TestCaseCatalogue"
                }
              }
            }
          },
          "403": {
            "description": "Not a sandbox key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/test-cases/{id}": {
      "get": {
        "operationId": "getTestCase",
        "summary": "Get one test",
        "description": "One test, as listed by GET /v1/test-cases. Sandbox key only: a live key, an internal key or a partner proxy key gets 403 (SANDBOX_ONLY or KEY_NOT_ALLOWED). An entity key sees only its own entity; an organisation key sees its entities (send x-entity-id to name one).",
        "tags": [
          "Integration Tests"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "The test.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TestCase"
                }
              }
            }
          },
          "403": {
            "description": "Not a sandbox key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such test (CASE_NOT_FOUND).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "operationId": "updateTestCase",
        "summary": "Change one of your tests",
        "description": "Changes one of your own tests; this creates a new version, and past runs keep the version they ran against. Any of the fields may be sent. Standard tests cannot be changed (403 CASE_NOT_EDITABLE). Sandbox key only: a live key, an internal key or a partner proxy key gets 403 (SANDBOX_ONLY or KEY_NOT_ALLOWED). An entity key sees only its own entity; an organisation key sees its entities (send x-entity-id to name one).",
        "tags": [
          "Integration Tests"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TestCaseRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The test was changed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TestCaseWritten"
                }
              }
            }
          },
          "400": {
            "description": "A field is invalid (INVALID_CASE).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Not a sandbox key, or a standard test (CASE_NOT_EDITABLE).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such test.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "archiveTestCase",
        "summary": "Remove one of your tests",
        "description": "Removes one of your own tests from new runs. Past runs keep showing it. Standard tests cannot be removed. Sandbox key only: a live key, an internal key or a partner proxy key gets 403 (SANDBOX_ONLY or KEY_NOT_ALLOWED). An entity key sees only its own entity; an organisation key sees its entities (send x-entity-id to name one).",
        "tags": [
          "Integration Tests"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "The test was archived.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TestCaseArchived"
                }
              }
            }
          },
          "403": {
            "description": "Not a sandbox key, or a standard test.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such test.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/receive/simulate": {
      "post": {
        "operationId": "simulateReceivedInvoices",
        "summary": "Create sample received invoices (sandbox)",
        "description": "Sandbox only: creates sample incoming (received) invoices or credit notes for this entity, to build and test an accounts-payable integration without waiting for a real supplier. Each document is a realistic e-invoice (Factur-X for France, ZUGFeRD for Germany) from a clearly fictional supplier, received through the same pipeline as a real received document: it is stored, archived and fires the invoice.received webhook, shows up in GET /v1/invoices with intakeChannel SANDBOX_SIMULATION, and no email is sent. A live key returns 403 SANDBOX_ONLY.",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SimulateReceivedRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The documents were created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SimulateReceivedResult"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body, COUNTRY_REQUIRED (several supported registrations: send country) or INVALID_BUSINESS_STATUS (the response lists the allowed set).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Not a sandbox key (SANDBOX_ONLY).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "COUNTRY_NOT_REGISTERED (the entity has no registration there) or COUNTRY_NOT_ENABLED (no sample documents for that country).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "description": "SIMULATION_FAILED: retry; createdIds lists what was already created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/receive": {
      "post": {
        "operationId": "receiveInvoiceDocument",
        "summary": "Submit a received (inbound) invoice document",
        "description": "Submit a received (inbound) ZUGFeRD PDF or bare CII/UBL XML document on this entity's behalf — for a supplier document that arrived outside Clearvo (email, a portal download, ...). DE only today; any other country returns 409 COUNTRY_NOT_ENABLED (with fixUrl). Runs the same detect → validate → archive → persist → webhook pipeline every DE inbound adapter shares. Always returns 200 for a successfully intaken document, conformant or not (never a 4xx for the document's own content) — see validationOutcome. A repeat call with byte-identical content returns the same id with duplicate:true rather than storing a second copy.",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "The PDF (ZUGFeRD hybrid) or XML (CII/UBL) document bytes."
                  },
                  "country": {
                    "type": "string",
                    "default": "DE"
                  }
                }
              }
            },
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "fileBase64"
                ],
                "properties": {
                  "fileBase64": {
                    "type": "string",
                    "description": "Base64-encoded document bytes."
                  },
                  "fileName": {
                    "type": "string",
                    "nullable": true
                  },
                  "contentType": {
                    "type": "string",
                    "nullable": true
                  },
                  "country": {
                    "type": "string",
                    "default": "DE"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The document was successfully intaken (conformant or not — see validationOutcome), or a duplicate short-circuit.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboundReceiveResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed multipart body (missing file field), or invalid JSON (missing/empty fileBase64, or not valid base64).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "COUNTRY_NOT_ENABLED — inbound receiving is not yet available for the given country. Carries fixUrl.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "FILE_TOO_LARGE — the document exceeds the inbound size cap.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "UNSUPPORTED_MEDIA_TYPE — the declared filename/content-type is clearly not a PDF or XML candidate.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "MALWARE_DETECTED — the uploaded document failed a malware scan and was rejected; nothing was stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "INGEST_FAILED — the ingestion pipeline failed; retryable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/receive/bulk": {
      "post": {
        "operationId": "receiveInvoiceDocumentsBulk",
        "summary": "Submit many received invoice documents at once (async)",
        "description": "Accepts a single file field (a .zip archive) or one or more files fields (individual documents), normalizes into one canonical zip blob, and processes asynchronously through the exact same pipeline POST /receive uses. Poll GET /receive/bulk/{batchId} for progress and final per-outcome counts.",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "A .zip archive of documents."
                  },
                  "files": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "binary"
                    },
                    "description": "One or more individual PDF/XML documents — mutually exclusive with file."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted for async processing.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-RateLimit-Scope": {
                "$ref": "#/components/headers/X-RateLimit-Scope"
              },
              "X-RateLimit-Rows-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Rows-Limit"
              },
              "X-RateLimit-Rows-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Rows-Remaining"
              },
              "X-RateLimit-Rows-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Rows-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboundBulkBatchStatus"
                }
              }
            }
          },
          "200": {
            "description": "Idempotent replay — an identical upload was already landed as an existing batch.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-RateLimit-Scope": {
                "$ref": "#/components/headers/X-RateLimit-Scope"
              },
              "X-RateLimit-Rows-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Rows-Limit"
              },
              "X-RateLimit-Rows-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Rows-Remaining"
              },
              "X-RateLimit-Rows-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Rows-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboundBulkBatchStatus"
                }
              }
            }
          },
          "400": {
            "description": "No files provided, or the file field is not a valid .zip archive.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "411": {
            "description": "Content-Length is required — chunked transfer encoding is not supported on this endpoint."
          },
          "413": {
            "description": "FILE_TOO_LARGE (total upload over the cap) or TOO_MANY_FILES.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/receive/bulk/{batchId}": {
      "get": {
        "operationId": "getInboundBatch",
        "summary": "Poll one async inbound bulk-receive batch's status",
        "description": "Same underlying tracker/shape as GET /send/bulk-async/{batchId} — entityId is resolved from the caller's own API key, so a batch belonging to a different entity is a plain 404, never a cross-tenant lookup.",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "batchId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboundBulkBatchStatus"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Batch not found, or not owned by the caller's entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/de/inbound-address": {
      "get": {
        "operationId": "getInboundEmailAddress",
        "summary": "Get this entity's DE inbound forwarding email address",
        "description": "Read-only mirror of the dashboard's DE receiving setup — the correct \"dedicated per-entity identifier\" pattern for resolving an unauthenticated inbound channel to an entity, never content-sniffing. address is null until one has been issued from the dashboard (Country Setup → Germany); forward supplier invoices there to have them ingested automatically through the same pipeline as POST /receive. Issuing/regenerating one is not yet a public-API action.",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InboundEmailAddressStatus"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/mx/credentials": {
      "post": {
        "operationId": "saveMxCredentials",
        "summary": "Register or update the entity's SAT e.firma (sat_pull mode)",
        "description": "The entity must already carry a REGISTERED Mexico tax registration (its RFC) before an e.firma can be uploaded — this route only cross-checks the certificate's own RFC against it, it never writes the RFC. Refused outright for a sandbox key: there is no SAT sandbox to verify against. Only needed for sat_pull ingestion mode (the default) — client_push entities never need this.",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "certificateBase64",
                  "keyBase64",
                  "password"
                ],
                "properties": {
                  "certificateBase64": {
                    "type": "string",
                    "description": "base64(DER-encoded X.509 .cer file, as issued by SAT)."
                  },
                  "keyBase64": {
                    "type": "string",
                    "description": "base64(DER-encoded, password-encrypted PKCS#8 .key file)."
                  },
                  "password": {
                    "type": "string",
                    "description": "The e.firma's password — never stored.",
                    "writeOnly": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Credential registered.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MxCredentialStatus"
                }
              }
            }
          },
          "400": {
            "description": "Missing/invalid fields, an invalid e.firma, no Mexico tax registration on file yet for this entity, an RFC mismatch against the entity's own registration, or a sandbox API key (no SAT sandbox exists).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "get": {
        "operationId": "getMxCredentialStatus",
        "summary": "Retrieve the stored e.firma's status (no key material)",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MxCredentialStatus"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No MX credentials stored for this entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "deleteMxCredentials",
        "summary": "Remove the stored e.firma",
        "description": "Does not change mx_ingestion_mode — an entity left on sat_pull with no credential simply stops being polled until a new one is uploaded, or the entity is switched to client_push.",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No MX credentials stored for this entity to delete.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/mx/inbound/cfdi": {
      "post": {
        "operationId": "pushMxCfdi",
        "summary": "Push a received CFDI 4.0 document (client_push ingestion)",
        "description": "Always accepted regardless of the entity's mxIngestionMode — mode only governs whether the SAT-pull poller sweeps the entity. Idempotent on the CFDI's own UUID (Folio Fiscal): a retried push for an already-terminal document returns 200 with duplicate:true, never 409. Never throws a generic 500 for a malformed/rejected document — every outcome is a structured, coded response. Runs the same validation pipeline as the SAT-pull path: CFDI 4.0/Anexo 20 schema check, digital seal verification, SAT's own ConsultarEstatusCFDI status check, and a UsoCFDI/RegimenFiscalReceptor compatibility flag against the receiving entity's registered regime. Tolerates Carta Porte / Complemento de Recepción de Pagos complements being present without extracting their specific fields (Phase 1 scope).",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/xml": {
              "schema": {
                "type": "string",
                "description": "The raw, byte-identical CFDI 4.0 XML as received from the issuer's PAC."
              }
            },
            "text/xml": {
              "schema": {
                "type": "string"
              }
            },
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "documentXml"
                ],
                "properties": {
                  "documentXml": {
                    "type": "string",
                    "description": "The raw CFDI 4.0 XML, as a JSON string field."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Terminal outcome (SAT answered Vigente/ACCEPTED or Cancelado/CANCELLED), including a duplicate short-circuit.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MxInboundCfdiResult"
                }
              }
            }
          },
          "202": {
            "description": "Non-terminal PENDING — SAT's ConsultarEstatusCFDI answered \"No Encontrado\" (not yet published on SAT's own backend) or was itself unreachable. Never a rejection either way — the SAT-pull poll job re-checks this UUID on its own schedule, and the same client_push call can simply be retried later (idempotent on UUID, never a duplicate insert).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MxInboundCfdiResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request (invalid JSON, missing/empty documentXml), a <!DOCTYPE declaration (XXE hard-reject, never valid in a CFDI), or no Mexico tax registration (RFC) on file for this entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "The document exceeds the 2MB maximum size.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Structurally invalid CFDI (unsupported version — only 4.0 is accepted, missing required fields, missing/invalid TFD), the generic \"público en general\"/\"extranjero sin RFC\" placeholder RFC (XAXX010101000/XEXX010101000), or the CFDI's Receptor/@Rfc doesn't match this entity's own registered RFC — never a real receiving taxpayer, and never silently accepted cross-entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/mx/inbound/poll": {
      "post": {
        "operationId": "pollMxSat",
        "summary": "Trigger a SAT Descarga Masiva poll cycle",
        "description": "Two auth modes on the same route (mirrors Poland KSeF's and France's own inbound poll routes): an x-cron-secret header sweeps every eligible entity (the automated hourly background job); an x-api-key polls only the calling entity, rate-limited to once per 15 minutes per entity so a client retry loop can't burn SAT's own request budget — a second manual call inside that window gets 429, never silently re-queued or ignored. The automated background sweep is a separate path and is never throttled by this per-entity cooldown. Uses a metadata-first rolling lookback window against SAT's Descarga Masiva service — never an incremental cursor — to tolerate SAT's own multi-day backend indexing lag without permanently missing a document. Each newly received invoice is stored with direction=\"inbound\" (list via GET /invoices?direction=inbound, or filter with receivedAfter) and fires an invoice.received webhook. Requires an API key with write access (polling ingests invoices into your inbox); a read-only key is refused with 403.",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Poll cycle result (cron sweep summary, or this one entity's own outcome for a manual call).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MxPollResult"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key / cron secret",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "A manual (x-api-key) poll was already triggered for this entity within the last 15 minutes. Also returned, with the RateLimited envelope (`error.code` RATE_LIMIT_EXCEEDED), when this API key's per-minute limit for heavy operations (60/min) is exceeded — see the Rate limits section of the API overview.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds until the cooldown clears."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/mx/sync-status": {
      "get": {
        "operationId": "getMxSyncStatus",
        "summary": "SAT-pull job health for this entity",
        "description": "Pure DB read — never calls SAT itself. Explains SAT's own multi-day backend lag instead of an unexplained empty Incoming tab. Always not_started for a client_push-only entity (there is no poll job to report on for that mode).",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MxSyncStatus"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/hu/credentials": {
      "post": {
        "operationId": "setHuCredentials",
        "summary": "Register Hungary NAV credentials",
        "description": "Register or update NAV Online Számla credentials for an entity: tax number and technical user details. Required before submitting invoices to Hungary. The technical user is created in the NAV Online Számla portal (onlineszamla.nav.gov.hu) with invoice reporting rights. All fields are required on every call — this endpoint does not support a partial update.",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "taxNumber": {
                    "type": "string",
                    "description": "8-digit Hungarian taxpayer ID.",
                    "example": "12345678"
                  },
                  "login": {
                    "type": "string",
                    "description": "NAV technical user login."
                  },
                  "password": {
                    "type": "string",
                    "description": "NAV technical user password."
                  },
                  "signKey": {
                    "type": "string",
                    "description": "Signature key from the NAV technical user registration."
                  },
                  "exchangeKey": {
                    "type": "string",
                    "description": "16-character alphanumeric AES-128-ECB token decryption key (used directly as raw key bytes, not hex-encoded)."
                  },
                  "environment": {
                    "type": "string",
                    "enum": [
                      "production",
                      "test"
                    ],
                    "description": "Defaults to \"production\". A sandbox API key must use \"test\" — a sandbox-scoped request with \"production\" (or the omitted default) is rejected with 400."
                  }
                },
                "required": [
                  "taxNumber",
                  "login",
                  "password",
                  "signKey",
                  "exchangeKey"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Credentials registered",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "taxNumber": {
                      "type": "string"
                    },
                    "environment": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "get": {
        "operationId": "getHuCredentials",
        "summary": "Get Hungary NAV credentials",
        "description": "Retrieve registered NAV credentials for an entity. Secret fields (password, signKey, exchangeKey) are never returned.",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "environment",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "production",
                "test"
              ]
            },
            "description": "Defaults to \"production\"."
          }
        ],
        "responses": {
          "200": {
            "description": "Registered credentials",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "taxNumber": {
                      "type": "string"
                    },
                    "login": {
                      "type": "string"
                    },
                    "environment": {
                      "type": "string"
                    },
                    "createdAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updatedAt": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid environment query parameter",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No HU credentials found for this entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "deleteHuCredentials",
        "summary": "Remove Hungary NAV credentials",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "environment",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "production",
                "test"
              ]
            },
            "description": "Defaults to \"production\"."
          }
        ],
        "responses": {
          "200": {
            "description": "Credentials deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid environment query parameter",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No HU credentials found for this entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/gr/credentials": {
      "post": {
        "operationId": "setGrCredentials",
        "summary": "Register Greece AADE myDATA credentials",
        "description": "Register or update AADE myDATA credentials for an entity: ΑΦΜ (AFM), AADE user ID, and subscription key. Required before submitting invoices to Greece. Issue the credentials via the AADE developer portal (aade.gr/epicheireseis/e-timologisi/mydata).",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "afm": {
                    "type": "string",
                    "description": "9-digit Greek ΑΦΜ (tax registration number), no spaces.",
                    "example": "123456789"
                  },
                  "userId": {
                    "type": "string",
                    "description": "AADE myDATA user ID."
                  },
                  "subscriptionKey": {
                    "type": "string",
                    "description": "Ocp-Apim-Subscription-Key from the AADE developer portal."
                  }
                },
                "required": [
                  "afm",
                  "userId",
                  "subscriptionKey"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Credentials registered",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "afm": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "get": {
        "operationId": "getGrCredentials",
        "summary": "Get Greece AADE myDATA credentials",
        "description": "Retrieve registered AADE credentials for an entity. The subscription key is never returned.",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "Registered credentials",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "afm": {
                      "type": "string"
                    },
                    "userId": {
                      "type": "string"
                    },
                    "createdAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updatedAt": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No GR credentials found for this entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "deleteGrCredentials",
        "summary": "Remove Greece AADE myDATA credentials",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "Credentials deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No GR credentials found for this entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/ro/credentials": {
      "post": {
        "operationId": "setRoCredentials",
        "summary": "Register Romania ANAF e-Factura credentials",
        "description": "Register or update ANAF e-Factura credentials for an entity: CUI (Cod de Identificare Fiscala) and an ANAF SPV OAuth2 Bearer token. Required before submitting invoices to Romania. A sandbox API key stores a test-environment token, which Clearvo uses to submit to ANAF's real test endpoint (not a mock); a live API key stores a production token. ANAF access tokens are valid for months but do expire and are not refreshed automatically — register a fresh token before it expires.",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "cui": {
                    "type": "string",
                    "description": "Romanian fiscal code (Cod de Identificare Fiscala). The RO prefix is optional and normalized on storage.",
                    "example": "RO12345678"
                  },
                  "token": {
                    "type": "string",
                    "description": "OAuth2 Bearer token issued by ANAF SPV (Spațiul Privat Virtual) with e-Factura upload rights."
                  },
                  "environment": {
                    "type": "string",
                    "enum": ["production", "test"],
                    "description": "Optional. Derived from the API key (sandbox keys are 'test', live keys are 'production'); if supplied it must match, otherwise the request is rejected with 400."
                  }
                },
                "required": [
                  "cui",
                  "token"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Credentials registered",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "cui": {
                      "type": "string"
                    },
                    "environment": {
                      "type": "string",
                      "enum": ["production", "test"]
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "get": {
        "operationId": "getRoCredentials",
        "summary": "Get Romania ANAF e-Factura credentials",
        "description": "Retrieve registered ANAF credentials for an entity. The token is never returned.",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "Registered credentials",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "cui": {
                      "type": "string"
                    },
                    "environment": {
                      "type": "string",
                      "enum": ["production", "test"]
                    },
                    "createdAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updatedAt": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No RO credentials found for this entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "deleteRoCredentials",
        "summary": "Remove Romania ANAF e-Factura credentials",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "Credentials deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No RO credentials found for this entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/eg/credentials": {
      "post": {
        "operationId": "setEgCredentials",
        "summary": "Register Egypt ETA credentials",
        "description": "Register or update Egyptian Tax Authority (ETA) e-invoicing credentials for an entity: ETA Registration Number, OAuth2 client credentials, and the entity's own eSeal certificate. Required before submitting invoices to Egypt. Live-verifies the OAuth2 credentials against the real ETA before saving (skipped for sandbox API keys, which never call the real authority) — a failed verification is rejected with 400 rather than stored.",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "registrationNumber": {
                    "type": "string",
                    "description": "9-digit ETA Registration Number.",
                    "example": "123456789"
                  },
                  "clientId": {
                    "type": "string",
                    "description": "ETA OAuth2 Client ID, issued when this entity registers Clearvo as its ERP/representative on the ETA taxpayer portal."
                  },
                  "clientSecret": {
                    "type": "string",
                    "description": "ETA OAuth2 Client Secret."
                  },
                  "certPem": {
                    "type": "string",
                    "description": "The entity's own eSeal X.509 certificate (PEM)."
                  },
                  "keyPem": {
                    "type": "string",
                    "description": "RSA private key matching certPem (PEM)."
                  }
                },
                "required": [
                  "registrationNumber",
                  "clientId",
                  "clientSecret",
                  "certPem",
                  "keyPem"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Credentials registered",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "registrationNumber": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed, or live ETA verification failed (code ETA_VERIFICATION_FAILED — skipped for sandbox keys).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Failed to store credentials (code DB_ERROR).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "get": {
        "operationId": "getEgCredentials",
        "summary": "Get Egypt ETA credentials",
        "description": "Retrieve registered ETA credentials for an entity. clientSecret/certPem/keyPem are never returned — hasCertificate reports only whether a cert/key pair is on file.",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "Registered credentials",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "registrationNumber": {
                      "type": "string"
                    },
                    "clientId": {
                      "type": "string"
                    },
                    "hasCertificate": {
                      "type": "boolean"
                    },
                    "createdAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updatedAt": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No EG credentials found for this entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Failed to fetch credentials (code DB_ERROR).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "deleteEgCredentials",
        "summary": "Remove Egypt ETA credentials",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "Credentials deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No EG credentials found for this entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Failed to delete credentials (code DB_ERROR).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/il/credentials": {
      "post": {
        "operationId": "setIlCredentials",
        "summary": "Register Israel ITA credentials",
        "description": "Register or update Israel Tax Authority (ITA) e-invoicing credentials for an entity: the Israeli business/dealer number, the fixed identifier the ITA access token was issued under, and the access token itself. Required before requesting Invoice Allocation Numbers for Israel. Unlike PL/HU/GR, the entity must obtain its own ITA access token directly from the ITA via an interactive OAuth2 flow — there is no server-to-server flow Clearvo can drive on the customer's behalf. Live-verifies the token against the real ITA before saving (skipped for sandbox API keys) — a failed verification is rejected with 400 rather than stored.",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "vatNumber": {
                    "type": "string",
                    "description": "9-digit Israeli business/dealer number.",
                    "example": "123456789"
                  },
                  "userId": {
                    "type": "string",
                    "description": "The \"fixed identifier\" the ITA access token was issued under."
                  },
                  "accessToken": {
                    "type": "string",
                    "description": "Bearer token the customer obtains directly from the ITA via an interactive OAuth2 \"User Restricted\" flow."
                  }
                },
                "required": [
                  "vatNumber",
                  "userId",
                  "accessToken"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Credentials registered",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "vatNumber": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed, or live ITA verification failed (code ITA_VERIFICATION_FAILED — skipped for sandbox keys).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Failed to store credentials (code DB_ERROR).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "get": {
        "operationId": "getIlCredentials",
        "summary": "Get Israel ITA credentials",
        "description": "Retrieve registered ITA credentials for an entity. The access token is never returned.",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "Registered credentials",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "vatNumber": {
                      "type": "string"
                    },
                    "userId": {
                      "type": "string"
                    },
                    "createdAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updatedAt": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No IL credentials found for this entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Failed to fetch credentials (code DB_ERROR).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "deleteIlCredentials",
        "summary": "Remove Israel ITA credentials",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "Credentials deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No IL credentials found for this entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Failed to delete credentials (code DB_ERROR).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/jo/credentials": {
      "post": {
        "operationId": "setJoCredentials",
        "summary": "Register Jordan JoFotara credentials",
        "description": "Register or update JoFotara credentials for an entity: the ISTD taxpayer number and the Client ID/Income Source Sequence/Secret Key issued from the JoFotara portal's \"Integrate Device\" screen. Required before submitting invoices to Jordan. Unlike GR/HU/PL, no live verification call is made before saving — JoFotara exposes exactly one operation (real invoice submission) with no side-effect-free way to test a credential.",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "taxpayerNumber": {
                    "type": "string",
                    "description": "The entity's Jordanian ISTD taxpayer number."
                  },
                  "incomeSourceSequence": {
                    "type": "string",
                    "description": "Numeric income source sequence, from the JoFotara portal's \"Integrate Device\" screen (same table as Client ID/Secret Key)."
                  },
                  "clientId": {
                    "type": "string",
                    "description": "Client ID (\"Current ID\") from the JoFotara portal."
                  },
                  "secretKey": {
                    "type": "string",
                    "description": "Secret Key from the JoFotara portal."
                  }
                },
                "required": [
                  "taxpayerNumber",
                  "incomeSourceSequence",
                  "clientId",
                  "secretKey"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Credentials registered",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "taxpayerNumber": {
                      "type": "string"
                    },
                    "incomeSourceSequence": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Failed to store credentials (code DB_ERROR).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "get": {
        "operationId": "getJoCredentials",
        "summary": "Get Jordan JoFotara credentials",
        "description": "Retrieve registered JoFotara credentials for an entity. The secret key is never returned.",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "Registered credentials",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "taxpayerNumber": {
                      "type": "string"
                    },
                    "incomeSourceSequence": {
                      "type": "string"
                    },
                    "clientId": {
                      "type": "string"
                    },
                    "createdAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updatedAt": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No JO credentials found for this entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Failed to fetch credentials (code DB_ERROR).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "deleteJoCredentials",
        "summary": "Remove Jordan JoFotara credentials",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "Credentials deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No JO credentials found for this entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Failed to delete credentials (code DB_ERROR).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/br/credentials": {
      "post": {
        "operationId": "saveBrCredentials",
        "summary": "Register or update the entity's e-CNPJ A1 certificate",
        "description": "The entity must already carry a REGISTERED Brazil tax registration (its CNPJ) before an e-CNPJ can be uploaded — this route only cross-checks the certificate's own CNPJ root (first 8 characters, not full 14-character equality — one e-CNPJ legitimately covers every filial under the same root) against it; it never writes the CNPJ. Unlike Mexico, a sandbox API key is NOT refused — the credential is stored with environment 'test' and the poll/Manifestação paths run against fixture SEFAZ transport instead of a real SEFAZ call. A3 (hardware-token) certificates are refused outright (CERTIFICATE_A3_UNSUPPORTED) — only A1 (file-based, PKCS#12) certificates are supported.",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "pfxBase64",
                  "password",
                  "consent"
                ],
                "properties": {
                  "pfxBase64": {
                    "type": "string",
                    "description": "Base64-encoded certificate file (.pfx) — the e-CNPJ A1 export."
                  },
                  "password": {
                    "type": "string",
                    "description": "The .pfx password — never stored.",
                    "writeOnly": true
                  },
                  "consent": {
                    "type": "boolean",
                    "description": "Must be true — confirms authorization for Clearvo to use this certificate to fetch invoices addressed to this CNPJ and to record this entity's own Manifestação responses with SEFAZ on its behalf. Required before any certificate material is decoded (400 CONSENT_REQUIRED otherwise)."
                  },
                  "autoCiencia": {
                    "type": "boolean",
                    "default": true,
                    "description": "When true (the default), Clearvo automatically registers Ciência da Operação with SEFAZ as soon as an invoice addressed to this CNPJ is discovered — this is what releases the full procNFe document. Registering awareness starts the statutory 90-day conclusive-response clock; it never confirms, disputes, or pays an invoice on the entity's behalf. Can be changed later without re-uploading the certificate via PATCH /br/settings."
                  },
                  "manifestationRegime": {
                    "type": "string",
                    "enum": [
                      "general"
                    ],
                    "default": "general",
                    "description": "The Manifestação deadline-policy regime for this entity — 'general' (Ciência 10 days / conclusive response 90 days, both from SEFAZ's own receipt timestamp) is the only value currently supported. Any other value, most notably 'anexo_ii' (the banded sector deadlines for fuel/TRR/bulk-alcohol distributors), is refused with 400 NOT_YET_SUPPORTED."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Credential registered.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BrCredentialStatus"
                }
              }
            }
          },
          "400": {
            "description": "Missing/invalid fields, no Brazil tax registration on file (REGISTRATION_REQUIRED), consent not given (CONSENT_REQUIRED), an unsupported manifestationRegime (NOT_YET_SUPPORTED), invalid base64 (INVALID_BASE64), or an invalid e-CNPJ (INVALID_PKCS12, PKCS12_PASSWORD_INVALID, CERTIFICATE_A3_UNSUPPORTED, CERTIFICATE_NOT_ECNPJ, CERTIFICATE_EXPIRED).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "The uploaded certificate's CNPJ root doesn't match this entity's own registered CNPJ (CERTIFICATE_CNPJ_MISMATCH).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "get": {
        "operationId": "getBrCredentialStatus",
        "summary": "Retrieve the stored e-CNPJ's status (no key material)",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BrCredentialStatus"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No BR credentials stored for this entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "deleteBrCredentials",
        "summary": "Remove the stored e-CNPJ",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No BR credentials stored for this entity to delete.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/br/inbound/poll": {
      "post": {
        "operationId": "pollBrDistribuicaoDfe",
        "summary": "Trigger a Distribuição DFe poll cycle",
        "description": "Two auth modes on the same route (mirrors Mexico's and Poland KSeF's own inbound poll routes): an x-cron-secret header sweeps every eligible entity (the automated hourly background job); an x-api-key polls only the calling entity, rate-limited to once per 60 minutes per entity (stricter than Mexico's 15-minute cooldown — SEFAZ's own shared rate budget is tighter) — a second manual call inside that window gets 429 (POLL_COOLDOWN_ACTIVE). Independently of the per-entity cooldown, the shared rate budget for the certificate's own CNPJ root (20 queries/hour, shared across every entity on that root) may already be parked — a recent 'no new documents' response, cStat 137 — or held — three consecutive rate-limit responses, cStat 656, a hard circuit breaker an administrator must release — in which case a manual trigger still answers 429 (RATE_BUDGET_PARKED / RATE_BUDGET_MANUAL_HOLD) with a Retry-After header, before any real SEFAZ call is attempted. Requires an API key with write access; a read-only key is refused with 403.",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Poll cycle result (cron sweep, or this one entity's own outcome for a manual call).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BrPollResult"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key / cron secret",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is read-only — polling ingests invoices into your inbox and requires write access.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "A manual (x-api-key) poll was already triggered for this entity within the last 60 minutes (POLL_COOLDOWN_ACTIVE), or the shared SEFAZ rate budget for this certificate's CNPJ root is currently parked (RATE_BUDGET_PARKED) or held after repeated rate-limit responses (RATE_BUDGET_MANUAL_HOLD — an administrator must release the hold).",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds until the cooldown/park clears — absent for RATE_BUDGET_MANUAL_HOLD."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/br/sync-status": {
      "get": {
        "operationId": "getBrSyncStatus",
        "summary": "Distribuição DFe poll job health for this entity",
        "description": "Pure DB read — never calls SEFAZ itself. Reads not_started until a real homologação (test-environment) run has verified this entity's credential end-to-end — before that, no manifestation is registered and no document is collected on the entity's behalf, though the underlying statutory deadlines are never paused by this state. A recent SEFAZ rate-limit park reads as ok, never stale/error — it is a normal, self-clearing condition, not a fault.",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BrSyncStatus"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/br/settings": {
      "patch": {
        "operationId": "updateBrSettings",
        "summary": "Flip auto-Ciência without re-uploading the certificate",
        "description": "The one standalone way to turn Ciência auto-registration on or off for an entity that already has a certificate on file — never requires the e-CNPJ certificate or password to be re-submitted (POST /br/credentials's own autoCiencia field only sets the STARTING value, at upload time). Turning auto-Ciência off never stops the day-7 Ciência-due reminder or the day-90 conclusive-deadline reminder — both keep firing regardless of this setting, since the statutory deadline itself is never paused by it.",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "autoCiencia"
                ],
                "properties": {
                  "autoCiencia": {
                    "type": "boolean",
                    "description": "When true, Clearvo automatically registers Ciência da Operação as soon as an invoice addressed to this CNPJ is discovered. When false, Ciência is never registered automatically; the entity must register it directly (their ERP, the SEFAZ portal, or PATCH /invoices/{id}/business-status)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Setting updated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "autoCiencia": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing/invalid JSON body, or autoCiencia is not a boolean.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is read-only — a write-access key is required (this is a write, even though it never touches the stored certificate).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/my/credentials": {
      "post": {
        "operationId": "setMyCredentials",
        "summary": "Register/verify Malaysia MyInvois intermediary authorization",
        "description": "Verify and record that this entity has completed the one-time \"Add Intermediary\" authorization step for Taxually in its own MyInvois Portal. Unlike every other Authority Credentials route, there is no secret to store here — Taxually authenticates every MyInvois call with its own registered Intermediary client credentials; this endpoint only confirms the customer's TIN/BRN have authorized that access. Live-verifies against MyInvois before saving (skipped for sandbox API keys) — a failed verification is rejected with 400 rather than stored.",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "tin": {
                    "type": "string",
                    "description": "LHDN-issued Tax Identification Number. Format [CBD]\\d{7,10} or \\d{11,12}.",
                    "example": "C2584563200"
                  },
                  "brn": {
                    "type": "string",
                    "description": "12-digit SSM Business Registration Number.",
                    "example": "202501012345"
                  }
                },
                "required": [
                  "tin",
                  "brn"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Authorization verified and stored",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "tin": {
                      "type": "string"
                    },
                    "brn": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "ready"
                      ]
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed, or live MyInvois verification failed (code MYINVOIS_VERIFICATION_FAILED — the customer has not completed \"Add Intermediary\" for Taxually in their own MyInvois Portal; skipped for sandbox keys).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Failed to store authorization (code DB_ERROR).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "get": {
        "operationId": "getMyCredentials",
        "summary": "Get Malaysia MyInvois authorization status",
        "description": "Retrieve this entity's stored MyInvois intermediary authorization status.",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "Authorization status",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "tin": {
                      "type": "string"
                    },
                    "brn": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "ready"
                      ]
                    },
                    "checkedAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updatedAt": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No MY authorization found for this entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Failed to fetch authorization (code DB_ERROR).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "deleteMyCredentials",
        "summary": "Remove Malaysia MyInvois authorization",
        "tags": [
          "Authority Credentials"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "Authorization deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No MY authorization found for this entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Failed to delete authorization (code DB_ERROR).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/team/invites": {
      "post": {
        "operationId": "inviteTeamMember",
        "summary": "Invite a team member",
        "description": "Invite a teammate to this Clearvo account by email. Requires an organisation-scoped API key — an entity-scoped key has no concept of \"the account's team\" and is rejected with 403. The invitee receives an email with a link to join and set up their own login. Does not support role=admin — see the role field description.",
        "tags": [
          "Team"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "Invitee's email address."
                  },
                  "role": {
                    "type": "string",
                    "enum": [
                      "developer",
                      "finance",
                      "viewer",
                      "auditor"
                    ],
                    "description": "Role granted to the invited member. \"admin\" is not available via the public API — any account member can mint an organisation-scoped write API key, so allowing admin here would let a lower-privileged member self-escalate. Invite admins from the dashboard's Team settings instead, where the caller's own role is checked."
                  },
                  "entityIds": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "description": "Optional — restrict the invited member to these entity IDs. Omit for access to every entity on the account."
                  }
                },
                "required": [
                  "email",
                  "role"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Invite created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "inviteId": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "email": {
                      "type": "string"
                    },
                    "role": {
                      "type": "string"
                    },
                    "entityIds": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "nullable": true
                    },
                    "expiresAt": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Entity-scoped API keys cannot invite team members. Also returned when the entityIds list includes an entity outside an organisation-scoped key's entity allowlist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "code=ALREADY_INVITED: a pending invite already exists for this email, or the email is already an active team member. code=SEAT_LIMIT_EXCEEDED: this organisation has reached its Clerk-configured team member limit (counting active members plus outstanding invites) — remove a member/invite or contact support to raise the limit.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Missing/invalid email or role, or an entityId does not belong to this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/requirements": {
      "get": {
        "operationId": "getRequirements",
        "summary": "Get country e-invoicing requirements",
        "description": "Returns e-invoicing requirements for a given country, including whether e-invoicing is mandatory, the mandatory date (if applicable), supported document types, Peppol scheme ID, VAT number format, the responsible tax authority, and country-specific notes. Unknown countries return a minimal fallback — treat them as \"check local regulations\".",
        "tags": [
          "Entities"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "country",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 2,
              "maxLength": 2
            },
            "description": "ISO 3166-1 alpha-2 country code (e.g. DE, IT, PL)."
          }
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Country requirements",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "country": {
                      "type": "string"
                    },
                    "countryName": {
                      "type": "string"
                    },
                    "eInvoicingMandatory": {
                      "type": "boolean"
                    },
                    "mandatoryFrom": {
                      "type": "string",
                      "format": "date",
                      "nullable": true,
                      "description": "ISO date from which e-invoicing is mandatory. Present only when eInvoicingMandatory is true."
                    },
                    "supportedDocumentTypes": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "code": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "peppolScheme": {
                      "type": "string",
                      "nullable": true,
                      "description": "Peppol EAS scheme ID for this country (e.g. 0208 for Belgium)."
                    },
                    "vatNumberRequired": {
                      "type": "boolean"
                    },
                    "vatNumberFormat": {
                      "type": "string",
                      "nullable": true
                    },
                    "vatNumberRegex": {
                      "type": "string",
                      "nullable": true
                    },
                    "authority": {
                      "type": "string",
                      "nullable": true
                    },
                    "authorityPortal": {
                      "type": "string",
                      "format": "uri",
                      "nullable": true
                    },
                    "notes": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "country query parameter is required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/validations": {
      "get": {
        "operationId": "getValidations",
        "summary": "List every check POST /v1/send can emit",
        "description": "Request-time catalogue of every check this API can run against a submitted invoice — the machine-readable sibling to GET /v1/requirements. Merges two sources, generated fresh on every call: (1) the platform's own structural/shape and legal-mandate checks, seeded Global rules; (2) Clearvo's content-team-editable validation-rules registry, which can gain or lose entries without a deploy — call this endpoint fresh rather than caching it long-term. Omit `country` for the full, unfiltered catalogue across every destination country.",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "country",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "minLength": 2,
              "maxLength": 2
            },
            "description": "ISO 3166-1 alpha-2 destination country. Scopes the catalogue to checks reachable for that one country (universal checks plus ones scoped to it). Omit for every country."
          }
        ],
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "The check catalogue for the requested scope.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "country": {
                      "type": "string",
                      "nullable": true,
                      "description": "Echoes the requested ?country, uppercased. Null when the parameter was omitted (full catalogue)."
                    },
                    "rulesetVersion": {
                      "type": "integer",
                      "description": "Fingerprint of the exact registry rules included in this response — sum of every included rule's own version. Purely a function of the registry half; the structural half never changes without a deploy."
                    },
                    "checks": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ValidationCatalogueEntry"
                      }
                    }
                  },
                  "required": [
                    "country",
                    "rulesetVersion",
                    "checks"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "country must be a 2-character ISO 3166-1 alpha-2 code",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "hint": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "VALIDATION_UNAVAILABLE — the validation engine is temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tax/exemptions/{id}/document": {
      "post": {
        "operationId": "uploadExemptionDocument",
        "summary": "Upload certificate document",
        "description": "Upload a PDF copy of the exemption certificate. The returned `documentUrl` is stored on the certificate record and can also be set via PATCH.",
        "tags": [
          "Exemption Certificates"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "document"
                ],
                "properties": {
                  "document": {
                    "type": "string",
                    "format": "binary",
                    "description": "PDF file of the exemption certificate."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "200": {
            "description": "Document uploaded",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": true
                    },
                    "documentUrl": {
                      "type": "string",
                      "format": "uri"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Not multipart/form-data, or no \"document\" field in the multipart body",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Certificate not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid file type — only PDF files are accepted (content-type application/pdf or a .pdf extension)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "501": {
            "description": "Document storage not configured for this environment",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/health": {
      "get": {
        "operationId": "getHealth",
        "summary": "Availability check",
        "description": "Public, unauthenticated liveness/readiness check for API consumers — reports whether both the production and sandbox database pools are reachable. Distinct from the infrastructure-level `/api/health` probe (process liveness only, no DB dependency, used by the platform's own deploy health gates) — this endpoint is dependency-checking and intended for API integrators to poll, not for infra probes.",
        "tags": [
          "Setup"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Both database pools are reachable",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "ok"
                      ]
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "One or both database pools are unreachable",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "degraded"
                      ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/fr/transactions/export": {
      "get": {
        "operationId": "exportFrTransactions",
        "summary": "Export France mandate transactions",
        "description": "Streaming, per-row export of this entity's canonical mandate_transactions rows for France — the audit-defense counterpart to GET /mandate-transactions, built for pulling a full décade/month/quarter's worth of rows rather than paging a UI. `period` (in the entity's own e-reporting cadence) and `from`/`to` are mutually exclusive ways to bound the date range; omit both to export everything. Streams `text/csv` or `application/x-ndjson` with a Content-Disposition attachment header — there is no JSON envelope on success.",
        "tags": [
          "Tax Reporting"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "uploadBatchId",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Restrict to one bulk-upload batch (see POST /send/bulk / POST /send/bulk-async)."
          },
          {
            "name": "period",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "A décade/month/quarter period code in this entity's own e-reporting cadence (e.g. 2026-06-D1, 2026-06, 2026-Q2). Mutually exclusive with from/to."
          },
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Transaction date range start (inclusive), YYYY-MM-DD. Mutually exclusive with period."
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Transaction date range end (inclusive), YYYY-MM-DD."
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated subset of: PENDING, RESOLVED, NEEDS_INFO, HELD_UNKNOWN_MANDATE, ACCUMULATED, CLEARED, HELD, NO_OBLIGATION."
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "csv",
                "jsonl"
              ],
              "default": "csv"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Streamed file download (attachment) of matching transactions.",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              },
              "application/x-ndjson": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "from/to not YYYY-MM-DD, period combined with from/to, period not valid for this entity's cadence, an unrecognised status value, or format not csv/jsonl.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity — either an entity-scoped key was used against a different entity, or an organisation-scoped key's entity allowlist does not include the entity named in x-entity-id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/mandates/obligations/{id}": {
      "patch": {
        "operationId": "acknowledgeMandateObligation",
        "summary": "Acknowledge a mandate obligation as handled elsewhere",
        "description": "Reversible acknowledgement that a compliance-radar mandate obligation is being handled outside Clearvo (e.g. via another provider) — flips its presented status/severity to HANDLED_ELSEWHERE (actionRequired: false) without asserting Clearvo has verified compliance. Set acknowledged back to false to clear it. Public-API counterpart of the dashboard's PATCH /api/tenant/compliance-radar/mandate-obligations/{id}. Distinct from GET /mandates, which is a static reference catalog, not a per-entity row.",
        "tags": [
          "Tax Reporting"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Mandate obligation ID."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "acknowledged"
                ],
                "properties": {
                  "acknowledged": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated mandate obligation, in the same shape as the mandateRows entries returned by the dashboard's compliance-radar endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Missing obligation id, invalid JSON body, or the body is not `{ acknowledged: boolean }`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Mandate obligation not found, or not owned by this account/entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Failed to update the mandate obligation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/send/bulk/{batchId}": {
      "get": {
        "operationId": "getBulkSendBatchStatus",
        "summary": "Get bulk CSV batch status",
        "description": "Status and progress for one batch submitted via POST /send/bulk — either the asynchronous transport (an upload of 25MB or more, which answers 202 with this batchId) or the synchronous transport's own duplicate/continuation hand-off. Same outcome vocabulary (ACCEPTED, ACCUMULATED, NEEDS_INFO, HELD, NO_OBLIGATION, SKIPPED_DUPLICATE, ERRORED) as the synchronous response's per-row outcome field, so results read identically regardless of which transport was used. A batch belonging to a different entity than the one resolved for this API key is a plain 404, never a cross-tenant lookup.",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "batchId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Batch status",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "batchId": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "UPLOADED",
                        "VALIDATING",
                        "COMPLETED",
                        "FAILED",
                        "CANCELLED"
                      ]
                    },
                    "filename": {
                      "type": "string",
                      "nullable": true
                    },
                    "uploadedBy": {
                      "type": "string",
                      "nullable": true
                    },
                    "total": {
                      "type": "integer"
                    },
                    "outcomeCounts": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "integer"
                      },
                      "description": "Per-outcome row counts. Same keys as POST /send/bulk's per-row outcome field — zero for any outcome this batch had no rows land in."
                    },
                    "summary": {
                      "type": "string",
                      "description": "e.g. \"4,102 added to a filing, 12 missing information, 3 errored\" — always derived from outcomeCounts."
                    },
                    "outcomeLabels": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "string"
                      },
                      "description": "Plain-English label per outcome key, the exact wording `summary` and every rendered chip are built from — the full static label set regardless of which outcomes this batch actually had."
                    },
                    "createdAt": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "validatedAt": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true
                    },
                    "completedAt": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Batch not found — either it doesn't exist, or it belongs to a different entity than the one resolved for this API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/tax/registrations/coverage-audit": {
      "get": {
        "operationId": "getRegistrationsCoverageAudit",
        "summary": "US home-rule locality coverage audit",
        "description": "For every curated US home-rule locality relevant to this entity, reports which mechanism (if any) covers it: an exact-city registration, a consolidated filing program enrollment (e.g. CO SUTS, AK ARSSTC), an Alabama SSUT flat-rate election, the entity's plain state-wide registration, or none of the above (gated and genuinely unregistered). Same computation and response shape as the dashboard's GET /api/tenant/registrations/coverage-audit.",
        "tags": [
          "Tax Registrations"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "entityId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Required for organisation-scoped keys; ignored for entity-scoped keys (the key's own entity wins)."
          },
          {
            "name": "asOf",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Evaluate coverage as of this date instead of now, respecting each mechanism's own effective window. YYYY-MM-DD."
          }
        ],
        "responses": {
          "200": {
            "description": "Per-locality coverage audit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "entityId": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "asOf": {
                      "type": "string",
                      "format": "date"
                    },
                    "localities": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "object",
                        "properties": {
                          "cchTaxAuthorityId": {
                            "type": "integer"
                          },
                          "region": {
                            "type": "string"
                          },
                          "locality": {
                            "type": "string",
                            "description": "Clearvo-controlled locality code, e.g. BOULDER."
                          },
                          "localityName": {
                            "type": "string",
                            "description": "Human-readable form, e.g. \"Boulder, CO\"."
                          },
                          "requiresOwnRegistration": {
                            "type": "boolean"
                          },
                          "classification": {
                            "type": "string",
                            "enum": [
                              "covered_by_program_enrollment",
                              "covered_by_exact_city_registration",
                              "covered_by_al_election",
                              "gated_and_unregistered"
                            ]
                          },
                          "covered": {
                            "type": "boolean",
                            "description": "Whether this locality is actually covered as of asOf — distinct from classification, which names the covering mechanism even in the rare edge case where it isn't currently active."
                          },
                          "detail": {
                            "type": "string"
                          },
                          "program": {
                            "type": "object",
                            "nullable": true,
                            "properties": {
                              "scheme": {
                                "type": "string"
                              },
                              "displayName": {
                                "type": "string"
                              },
                              "isMember": {
                                "type": "boolean"
                              },
                              "enrolled": {
                                "type": "boolean"
                              },
                              "memberFrom": {
                                "type": "string",
                                "nullable": true
                              },
                              "memberTo": {
                                "type": "string",
                                "nullable": true
                              },
                              "citation": {
                                "type": "string"
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "entityId query parameter missing for an organisation-scoped key, or asOf is not a valid YYYY-MM-DD date.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the entity specified in entityId.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Entity not found, or not owned by this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/rules-engine/schema": {
      "get": {
        "operationId": "getRulesEngineSchema",
        "summary": "Describe the rules-engine vocabulary",
        "description": "Domains, recordTypes, ruleKinds, statuses, operators, functions, and the closed condition/action property catalog. Call this before authoring any rule — a condition or action naming a property/operator this schema does not list is rejected with 422.",
        "tags": [
          "Rules Engine"
        ],
        "responses": {
          "200": {
            "description": "The rules-engine schema",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RulesEngineSchema"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/rules-engine/rules": {
      "get": {
        "operationId": "listRules",
        "summary": "List rules",
        "description": "This key's own rules — an entity-scoped key sees its Entity-scope rules, an organisation-scoped key (no x-entity-id) sees its Organisation-scope rules. Never the wider \"effective set\" a Global row would also contribute at evaluation time.",
        "tags": [
          "Rules Engine"
        ],
        "parameters": [
          {
            "name": "companyCode",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Only the rules scoped to this company code (case-insensitive). Omit to list every rule of the entity, company-code rules included (each carries companyCode)."
          },
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "domain",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "ap",
                "ar"
              ]
            }
          },
          {
            "name": "recordType",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "calculation",
                "purchase_order",
                "invoice"
              ]
            }
          },
          {
            "name": "direction",
            "in": "query",
            "description": "Filter to rules whose own transactionDirection condition(s) allow this direction (a rule with no transactionDirection condition matches both).",
            "schema": {
              "type": "string",
              "enum": [
                "purchase",
                "sale"
              ]
            }
          },
          {
            "name": "conditionProperty",
            "in": "query",
            "description": "Filter to rules that reference this property in one of their conditions.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "conditionValue",
            "in": "query",
            "description": "Used together with conditionProperty to filter by condition value.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "includeDefaults",
            "in": "query",
            "description": "Pass \"1\" to also include the platform-default rows (`defaults`/`defaultsCount`) in the response.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The matching rules",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "rules": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Rule"
                      }
                    },
                    "defaults": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DefaultRuleItem"
                      },
                      "description": "Only present when `includeDefaults=1` was sent."
                    },
                    "defaultsCount": {
                      "type": "integer",
                      "description": "Only present when `includeDefaults=1` was sent."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "operationId": "createRule",
        "summary": "Create a rule",
        "description": "Always starts status=DRAFT (call POST .../activate separately to make it fire). `code` is unique per (domain, recordType, scope) — re-POSTing the same code+scope with a byte-identical body returns the EXISTING rule (200, created: false); a different body with the same code is 409 RULE_CODE_EXISTS.",
        "tags": [
          "Rules Engine"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateRuleInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Idempotent replay of an existing rule (created: false)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "rule": {
                      "$ref": "#/components/schemas/Rule"
                    },
                    "created": {
                      "type": "boolean"
                    },
                    "deprecations": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "RE-9 — present (non-empty) only when the caller POSTed the deprecated `domain` field and it got translated into a transactionDirection condition. Omitted, not an empty array, when there's nothing to report."
                    }
                  }
                }
              }
            }
          },
          "201": {
            "description": "Rule created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "rule": {
                      "$ref": "#/components/schemas/Rule"
                    },
                    "created": {
                      "type": "boolean"
                    },
                    "deprecations": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "RE-9 — present (non-empty) only when the caller POSTed the deprecated `domain` field and it got translated into a transactionDirection condition. Omitted, not an empty array, when there's nothing to report."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "RULE_CODE_EXISTS — a different rule already has this code+scope with different content",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/rules-engine/rules/simulate": {
      "post": {
        "operationId": "simulateDraftRule",
        "summary": "Simulate a not-yet-saved rule",
        "description": "Preview a rule that has never been saved (read-only, no write) — `draft` is required. See POST .../rules/{id}/simulate to preview an already-saved rule instead.",
        "tags": [
          "Rules Engine"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "draft"
                ],
                "properties": {
                  "sampleSize": {
                    "type": "integer",
                    "description": "How many of the most recent records to sample against (1-500, default 50)."
                  },
                  "draft": {
                    "type": "object",
                    "properties": {
                      "domain": {
                        "type": "string",
                        "enum": [
                          "ap",
                          "ar"
                        ]
                      },
                      "recordType": {
                        "type": "string",
                        "enum": [
                          "calculation",
                          "purchase_order",
                          "invoice"
                        ]
                      },
                      "ruleKind": {
                        "type": "string",
                        "enum": [
                          "NORMALIZATION",
                          "ENRICHMENT",
                          "ADJUSTMENT",
                          "WARNING",
                          "ERROR"
                        ]
                      },
                      "checkpoint": {
                        "type": "string",
                        "enum": [
                          "PRE_CALC",
                          "POST_RATE_RESOLUTION"
                        ],
                        "default": "PRE_CALC",
                        "description": "Which pipeline checkpoint this rule attaches to. PRE_CALC (default) runs once, early, before jurisdiction/classification/rate resolution — today's only behavior for every pre-existing rule. POST_RATE_RESOLUTION runs immediately after the rate is resolved and may override it (ADJUSTMENT rule kind only)."
                      },
                      "conditions": {
                        "type": "array",
                        "items": {
                          "$ref": "#/components/schemas/RuleConditionItem"
                        }
                      },
                      "actions": {
                        "type": "array",
                        "items": {
                          "$ref": "#/components/schemas/RuleAction"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Simulation result",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "type": "object",
                      "properties": {
                        "ok": {
                          "type": "boolean"
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/SimulateRuleResult"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/rules-engine/rules/{id}": {
      "get": {
        "operationId": "getRule",
        "summary": "Read a rule",
        "description": "404 if it does not exist or belongs to a different scope.",
        "tags": [
          "Rules Engine"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The rule",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "rule": {
                      "$ref": "#/components/schemas/Rule"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or belongs to a different scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "operationId": "updateRule",
        "summary": "Edit a rule",
        "description": "`version` (optimistic concurrency — read it from GET first) is REQUIRED; a stale version returns 409 with the current version so you can reload and retry. `status` may only ever be set to ARCHIVED here directly. ACTIVE is reached via POST .../activate, or implicitly via `{ enabled: true }` on a currently-DRAFT rule (same transition, folded into this one call). There is still no real DELETE — `{ action: 'delete' }` retires a rule to ARCHIVED or PURGED instead, whichever the server determines is correct; see UpdateRuleInput and the Rule schema's `status` field for the full semantics.",
        "tags": [
          "Rules Engine"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateRuleInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated rule",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "rule": {
                      "$ref": "#/components/schemas/Rule"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Either this API key is not authorized for the specified entity, or (when `enabled: true` activates a DRAFT rule) this organisation requires a different person to activate a rule than the one who authored it (maker/checker).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or belongs to a different scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Version conflict — reload and retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/rules-engine/rules/{id}/activate": {
      "post": {
        "operationId": "activateRule",
        "summary": "Activate a rule",
        "description": "Move a DRAFT rule to ACTIVE. Idempotent if already ACTIVE; 409 if the rule is ARCHIVED or PURGED. Not the only path to ACTIVE any more — PATCH .../rules/{id} with `{ enabled: true }` on a DRAFT rule performs the identical transition inline, useful when you're already sending that call to turn a rule on and don't want a second round-trip. Use this endpoint instead when you want the activation to be explicit and separate from any other edit (e.g. a distinct \"Save & Activate\" action).",
        "tags": [
          "Rules Engine"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Activated (or already-ACTIVE) rule",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "rule": {
                      "$ref": "#/components/schemas/Rule"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Either this API key is not authorized for the specified entity, or this organisation requires a different person to activate a rule than the one who authored it (maker/checker).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or belongs to a different scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Cannot activate a rule that is not currently DRAFT (e.g. already ARCHIVED or PURGED)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/rules-engine/rules/{id}/suppress": {
      "post": {
        "operationId": "suppressRule",
        "summary": "Suppress a platform-default rule",
        "description": "RE-9 — opt this scope out of a wider-scope platform default (is_system) rule, without altering the default rule itself. `reason` is optional free text; a generic reason is recorded when none is given.",
        "tags": [
          "Rules Engine"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reason": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 2000,
                    "description": "Optional free-text justification for the suppression."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Suppressed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "suppressed": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Rule not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "details": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "path": {
                            "type": "string"
                          },
                          "message": {
                            "type": "string"
                          },
                          "code": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "operationId": "unsuppressRule",
        "summary": "Remove this scope's suppression of a platform-default rule",
        "description": "RE-9 — remove this scope's own suppression, if one exists. Does not affect a suppression held by a different (e.g. ancestor) scope.",
        "tags": [
          "Rules Engine"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Suppression removed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "suppressed": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No active suppression found for this rule at your scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/rules-engine/rules/{id}/move": {
      "post": {
        "operationId": "moveRule",
        "summary": "Reorder a rule",
        "description": "Reorder a rule within its own ruleKind by setting a new sortOrder (evaluation order is ruleKind, then sortOrder, then code).",
        "tags": [
          "Rules Engine"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "sortOrder"
                ],
                "properties": {
                  "sortOrder": {
                    "type": "integer"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Reordered rule",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "rule": {
                      "$ref": "#/components/schemas/Rule"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or belongs to a different scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/rules-engine/rules/{id}/versions": {
      "get": {
        "operationId": "listRuleVersions",
        "summary": "A rule's edit history",
        "description": "Its own rules_versions history (newest first) — one snapshot per create/update.",
        "tags": [
          "Rules Engine"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Version history",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "versions": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/RuleVersion"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or belongs to a different scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/rules-engine/rules/{id}/simulate": {
      "post": {
        "operationId": "simulateRule",
        "summary": "Simulate an existing rule",
        "description": "Preview a rule over the last `sampleSize` of this key's own records (read-only, no write) BEFORE activating it. Optionally pass `draft` to preview an in-flight edit before saving it.",
        "tags": [
          "Rules Engine"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "sampleSize": {
                    "type": "integer",
                    "description": "1-500, default 50."
                  },
                  "draft": {
                    "type": "object",
                    "description": "Optional override (domain/recordType/ruleKind/conditions/actions) to preview an in-flight edit.",
                    "properties": {
                      "domain": {
                        "type": "string",
                        "enum": [
                          "ap",
                          "ar"
                        ]
                      },
                      "recordType": {
                        "type": "string",
                        "enum": [
                          "calculation",
                          "purchase_order",
                          "invoice"
                        ]
                      },
                      "ruleKind": {
                        "type": "string",
                        "enum": [
                          "NORMALIZATION",
                          "ENRICHMENT",
                          "ADJUSTMENT",
                          "WARNING",
                          "ERROR"
                        ]
                      },
                      "checkpoint": {
                        "type": "string",
                        "enum": [
                          "PRE_CALC",
                          "POST_RATE_RESOLUTION"
                        ],
                        "default": "PRE_CALC",
                        "description": "Which pipeline checkpoint this rule attaches to. PRE_CALC (default) runs once, early, before jurisdiction/classification/rate resolution — today's only behavior for every pre-existing rule. POST_RATE_RESOLUTION runs immediately after the rate is resolved and may override it (ADJUSTMENT rule kind only)."
                      },
                      "conditions": {
                        "type": "array",
                        "items": {
                          "$ref": "#/components/schemas/RuleConditionItem"
                        }
                      },
                      "actions": {
                        "type": "array",
                        "items": {
                          "$ref": "#/components/schemas/RuleAction"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Simulation result",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "type": "object",
                      "properties": {
                        "ok": {
                          "type": "boolean"
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/SimulateRuleResult"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or belongs to a different scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/rules-engine/properties": {
      "get": {
        "operationId": "listRulePropertyDefinitions",
        "summary": "List custom property definitions",
        "description": "This key's own scope's custom property definitions. Built-in fields (glAccount, costCenter, intendedUse, accountAssignment, commodityCode, ...) are not custom properties: they are listed under `properties` of GET /rules-engine/schema.",
        "tags": [
          "Rules Engine"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "includeDeprecated",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false
            },
            "description": "true includes a deprecated definition in the list too. Default excludes it — the common case (rule-authoring pickers) shouldn't offer a retired field for new use."
          }
        ],
        "responses": {
          "200": {
            "description": "Property definitions",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "definitions": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/RulePropertyDefinition"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "operationId": "createRulePropertyDefinition",
        "summary": "Define a custom property",
        "description": "For data that is NOT part of the platform schema. Referenced by rules as \"customProperties.<propertyKey>\". 409 if propertyKey already exists at this scope. 422 if propertyKey equals a built-in field name (glAccount, costCenter, intendedUse, accountAssignment, commodityCode, commodityCodeScheme, ...): built-in fields are regular fields, never custom ones.",
        "tags": [
          "Rules Engine"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "propertyKey",
                  "label",
                  "dataType"
                ],
                "properties": {
                  "propertyKey": {
                    "type": "string",
                    "description": "Must start with a letter; letters/numbers/underscore only."
                  },
                  "label": {
                    "type": "string"
                  },
                  "dataType": {
                    "type": "string",
                    "enum": [
                      "string",
                      "number",
                      "boolean"
                    ]
                  },
                  "appliesTo": {
                    "type": "string",
                    "enum": [
                      "line",
                      "header"
                    ],
                    "default": "line"
                  },
                  "isWritable": {
                    "type": "boolean",
                    "default": true,
                    "description": "false marks a derived-output fact no rule action may ever target."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Property definition created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "definition": {
                      "$ref": "#/components/schemas/RulePropertyDefinition"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "propertyKey already exists at this scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed, including a propertyKey that equals a built-in field name (detail on path `propertyKey`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/rules-engine/properties/{id}": {
      "patch": {
        "operationId": "updateRulePropertyDefinition",
        "summary": "Edit a custom property's label, or deprecate/reactivate it",
        "description": "`propertyKey`/`dataType` are immutable once created and are never accepted here — create a new definition and deprecate the old one instead. `deprecate: true` deprecates (hides from new rule-authoring use, no effect on existing rules/values); `deprecate: false` reactivates. Omit `deprecate` to leave deprecation status unchanged. Scoped to a definition this key owns at its exact scope — a definition belonging to a different scope, returns 404.",
        "tags": [
          "Rules Engine"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "label": {
                    "type": "string"
                  },
                  "deprecate": {
                    "type": "boolean",
                    "description": "true to deprecate, false to reactivate. Omit to leave unchanged."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Property definition updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "definition": {
                      "$ref": "#/components/schemas/RulePropertyDefinition"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or not owned by this key's exact scope, or a platform (is_system) definition",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed (e.g. propertyKey/dataType sent, or empty body)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/rules-engine/templates": {
      "get": {
        "operationId": "listRulesEngineTemplates",
        "summary": "List starter rule templates",
        "description": "Visible to this key (its own scope plus every platform template). A template has a paramsSchema (GET .../templates/{id} for the detail) and is turned into a real rule via PUT .../templates/{id}. Includes three default shipping templates that fill a US shipping fact a transaction line omits (the shipping rules still decide the tax): default_shipping_carrier (param carrier: common or seller_vehicle), default_shipping_charge_avoidable (param chargeAvoidable: boolean) and default_shipping_actual_cost (param actualCostOfShipment: boolean). Templates for the Tax Calculations configuration surface are available on every plan; all other rules-engine features need Enterprise. Distinct from the fee-rules-engine feature's own GET /rule-templates.",
        "tags": [
          "Rules Engine"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "domain",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "ap",
                "ar"
              ]
            }
          },
          {
            "name": "recordType",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "calculation",
                "purchase_order",
                "invoice"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Templates",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "templates": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/RuleTemplate"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/rules-engine/templates/{id}": {
      "get": {
        "operationId": "getRulesEngineTemplate",
        "summary": "Read a template",
        "description": "Full detail, including its paramsSchema — the exact params PUT .../templates/{id} expects.",
        "tags": [
          "Rules Engine"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The template",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "template": {
                      "$ref": "#/components/schemas/RuleTemplate"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or belongs to a different scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "put": {
        "operationId": "instantiateRulesEngineTemplate",
        "summary": "Instantiate a template",
        "description": "Turn a template into a real rule (always DRAFT) by supplying its params (validated against the template's own paramsSchema — call GET .../templates/{id} first). 422 with the failing params.<key> path on a missing/mistyped param.",
        "tags": [
          "Rules Engine"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "params"
                ],
                "properties": {
                  "params": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Keyed by the template's own paramsSchema field names."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Reused an existing single-cardinality instance",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "rule": {
                      "$ref": "#/components/schemas/Rule"
                    },
                    "created": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "201": {
            "description": "New rule created from the template",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "rule": {
                      "$ref": "#/components/schemas/Rule"
                    },
                    "created": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or belongs to a different scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/rules-engine/datasets": {
      "get": {
        "operationId": "listRulesEngineDatasets",
        "summary": "List reference datasets",
        "description": "This key's own scope tuple's reference datasets (\"vlookups\").",
        "tags": [
          "Rules Engine"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "Datasets",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "datasets": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/RulesEngineDataset"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "operationId": "createRulesEngineDataset",
        "summary": "Create a reference dataset",
        "description": "Composite keys via keyColumns (e.g. [\"glAccount\",\"country\"]).",
        "tags": [
          "Rules Engine"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "datasetKey",
                  "name",
                  "keyColumns"
                ],
                "properties": {
                  "datasetKey": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "keyColumns": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "1-10 column names forming this dataset's composite key."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Idempotent replay of an existing dataset (created: false)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "dataset": {
                      "$ref": "#/components/schemas/RulesEngineDataset"
                    },
                    "created": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "201": {
            "description": "Dataset created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "dataset": {
                      "$ref": "#/components/schemas/RulesEngineDataset"
                    },
                    "created": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/rules-engine/datasets/{id}": {
      "get": {
        "operationId": "getRulesEngineDataset",
        "summary": "Read a dataset's metadata",
        "description": "keyColumns, rowCount.",
        "tags": [
          "Rules Engine"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The dataset",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "dataset": {
                      "$ref": "#/components/schemas/RulesEngineDataset"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or belongs to a different scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/rules-engine/datasets/{id}/rows": {
      "get": {
        "operationId": "listRulesEngineDatasetRows",
        "summary": "A page of a dataset's rows",
        "description": "A page of this reference dataset's own rows.",
        "tags": [
          "Rules Engine"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 500
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Rows",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "rows": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/RulesEngineDatasetRow"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "page": {
                      "type": "integer"
                    },
                    "limit": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or belongs to a different scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "operationId": "upsertRulesEngineDatasetRow",
        "summary": "Upsert a dataset row",
        "description": "Upsert one row by the dataset's own key_columns (never a separate row id — the composite key IS the identity).",
        "tags": [
          "Rules Engine"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "rowData"
                ],
                "properties": {
                  "rowData": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Must include a value for every one of the dataset's own key_columns."
                  },
                  "source": {
                    "type": "string",
                    "description": "Citation — required for a Global row."
                  },
                  "verified": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Idempotent replay / updated row (created: false)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "row": {
                      "$ref": "#/components/schemas/RulesEngineDatasetRow"
                    },
                    "created": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "201": {
            "description": "Row created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "row": {
                      "$ref": "#/components/schemas/RulesEngineDatasetRow"
                    },
                    "created": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or belongs to a different scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/rules-engine/datasets/{id}/rows/{rowKey}": {
      "delete": {
        "operationId": "deleteRulesEngineDatasetRow",
        "summary": "Delete a dataset row",
        "description": "Remove one row by its already-joined composite key string (the same value GET .../rows returns as each row's own rowKey).",
        "tags": [
          "Rules Engine"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "rowKey",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or belongs to a different scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/rules-engine/datasets/{id}/import": {
      "post": {
        "operationId": "importRulesEngineDataset",
        "summary": "Two-phase CSV import",
        "description": "dryRun (default true) computes and returns {adds,changes,deletes} without writing anything; call again with dryRun:false to apply that same diff (recomputed fresh — never a stale snapshot).",
        "tags": [
          "Rules Engine"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "csv"
                ],
                "properties": {
                  "csv": {
                    "type": "string",
                    "description": "Raw CSV text — header row + data rows."
                  },
                  "dryRun": {
                    "type": "boolean",
                    "default": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Dry-run diff, or the applied result",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "properties": {
                        "ok": {
                          "type": "boolean"
                        },
                        "dryRun": {
                          "type": "boolean",
                          "enum": [
                            true
                          ]
                        },
                        "adds": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "rowKey": {
                                "type": "string"
                              },
                              "rowData": {
                                "type": "object",
                                "additionalProperties": true
                              }
                            }
                          }
                        },
                        "changes": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "rowKey": {
                                "type": "string"
                              },
                              "rowData": {
                                "type": "object",
                                "additionalProperties": true
                              }
                            }
                          }
                        },
                        "deletes": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "rowKey": {
                                "type": "string"
                              },
                              "rowData": {
                                "type": "object",
                                "additionalProperties": true
                              }
                            }
                          }
                        }
                      }
                    },
                    {
                      "type": "object",
                      "properties": {
                        "ok": {
                          "type": "boolean"
                        },
                        "dryRun": {
                          "type": "boolean",
                          "enum": [
                            false
                          ]
                        },
                        "added": {
                          "type": "integer"
                        },
                        "changed": {
                          "type": "integer"
                        },
                        "deleted": {
                          "type": "integer"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or belongs to a different scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/rules-engine/datasets/{id}/export": {
      "get": {
        "operationId": "exportRulesEngineDataset",
        "summary": "Export a dataset as CSV",
        "description": "Raw CSV of every row in this dataset.",
        "tags": [
          "Rules Engine"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "CSV file",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or belongs to a different scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/rules-engine/mappings": {
      "get": {
        "operationId": "listFieldMappings",
        "summary": "List field mappings",
        "description": "An entity's own field-mapping rows for one source system (\"xero\" or \"coupa\"; the Coupa Call Out reads its invoice through the coupa mapping, whose companyCode row stays unmapped until you point it at the Coupa field carrying your company code) — the mapping-config replacement for that integration's hand-written date/address/country normalization. Field mappings are inherently per-entity.",
        "tags": [
          "Rules Engine"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "sourceSystem",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "xero"
          }
        ],
        "responses": {
          "200": {
            "description": "Field mappings",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "rules": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/FieldMapping"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Field mappings require an entity-scoped key (x-entity-id)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/rules-engine/mappings/{id}": {
      "get": {
        "operationId": "getFieldMapping",
        "summary": "Read a field mapping",
        "description": "Read one field-mapping row by id.",
        "tags": [
          "Rules Engine"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The field mapping",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "mapping": {
                      "$ref": "#/components/schemas/FieldMapping"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or belongs to a different scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "operationId": "updateFieldMapping",
        "summary": "Edit a field mapping",
        "description": "Edit sourcePath/transform/enabled/sortOrder, or retire it (status: ARCHIVED, the only status a PATCH may ever set). No optimistic-concurrency version on this resource.",
        "tags": [
          "Rules Engine"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "sourcePath": {
                    "type": "string"
                  },
                  "transform": {
                    "type": "object",
                    "required": [
                      "fn"
                    ],
                    "properties": {
                      "fn": {
                        "type": "string"
                      },
                      "args": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    }
                  },
                  "enabled": {
                    "type": "boolean"
                  },
                  "sortOrder": {
                    "type": "integer"
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "ARCHIVED"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated field mapping",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "mapping": {
                      "$ref": "#/components/schemas/FieldMapping"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or belongs to a different scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/rules-engine/mappings/reset": {
      "post": {
        "operationId": "resetFieldMappings",
        "summary": "Reset field mappings to defaults",
        "description": "Discard every one of this entity's own edits for one source system and re-instantiate fresh copies of the current Global starter template.",
        "tags": [
          "Rules Engine"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "sourceSystem",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "xero"
          }
        ],
        "responses": {
          "200": {
            "description": "The freshly re-instantiated field mappings",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "rules": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/FieldMapping"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Field mappings require an entity-scoped key (x-entity-id)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/rules-engine/trace": {
      "get": {
        "operationId": "getRulesTrace",
        "summary": "Full rules-engine execution trace",
        "description": "\"What changed before calculation\": the full rules-engine execution trace for one already-calculated record (a tax calculation, purchase order, or send-path invoice), in firing order. Never plan-gated.",
        "tags": [
          "Rules Engine"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "recordType",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "calculation",
                "purchase_order",
                "invoice"
              ]
            }
          },
          {
            "name": "recordId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The tax_calculations/einvoicing_records id (or purchase-order record id) to trace."
          }
        ],
        "responses": {
          "200": {
            "description": "The trace",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "recordType": {
                      "type": "string"
                    },
                    "recordId": {
                      "type": "string"
                    },
                    "entries": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/RulesTraceEntry"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/rules-engine/platform-changes": {
      "get": {
        "operationId": "listPlatformRuleChanges",
        "summary": "Recent platform (Global/Tenant) rule changes",
        "description": "Recent Global (Clearvo default) or Tenant (this white-label platform's own default) rule changes that apply to this key's calculations — never this key's OWN Entity/Organisation rules. Visible even before a change's own effectiveFrom date.",
        "tags": [
          "Rules Engine"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Platform rule changes",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "changes": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PlatformRuleChange"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/usage": {
      "get": {
        "operationId": "getUsage",
        "summary": "Get billable usage for the current billing period",
        "description": "Billable transaction usage against the plan allowance for the current billing period. Organisation-wide: the count covers every entity, so x-entity-id is not needed. Counts come from the hourly billing ledger and can trail live traffic by up to an hour (see usage.updatedAt). Sandbox calls are never counted.",
        "tags": [
          "Tax Calculations"
        ],
        "responses": {
          "200": {
            "description": "Usage summary",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "usage"
                  ],
                  "properties": {
                    "usage": {
                      "$ref": "#/components/schemas/Usage"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "description": "Failed to read usage",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/send/bulk/{batchId}/errors": {
      "get": {
        "operationId": "getBulkSendBatchErrors",
        "summary": "List structural errors for a bulk CSV batch",
        "description": "Paginated, machine-readable structural errors for one batch submitted via POST /send/bulk (rows rejected before they reached resolution). JSON only; there is no CSV export on this endpoint. A batch belonging to a different entity than the one resolved for this API key is a plain 404, never a cross-tenant lookup.",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "batchId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            },
            "description": "1-based page number."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            },
            "description": "Rows per page."
          }
        ],
        "responses": {
          "200": {
            "description": "A page of structural errors",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "batchId": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "UPLOADED",
                        "VALIDATING",
                        "COMPLETED",
                        "FAILED",
                        "CANCELLED"
                      ]
                    },
                    "page": {
                      "type": "integer"
                    },
                    "limit": {
                      "type": "integer"
                    },
                    "total": {
                      "type": "integer"
                    },
                    "errors": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "rowNumber": {
                            "type": "integer"
                          },
                          "errorCode": {
                            "type": "string"
                          },
                          "errorMessage": {
                            "type": "string",
                            "nullable": true
                          },
                          "rawRow": {
                            "type": "object",
                            "additionalProperties": true,
                            "description": "The offending CSV row as parsed."
                          },
                          "createdAt": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is not authorized for the specified entity.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Batch not found (BATCH_NOT_FOUND) — either it doesn't exist, or it belongs to a different entity than the one resolved for this API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/br/nfse/inbound/poll": {
      "post": {
        "operationId": "pollBrNfseAdn",
        "summary": "Trigger a NFS-e Nacional (ADN) poll cycle",
        "description": "Triggers a poll of Brazil's NFS-e Nacional contribuintes API (ADN) for received services invoices (NFS-e). Same two auth modes as POST /br/inbound/poll but a separate route with its own cooldown and rate budget: an x-cron-secret header sweeps every eligible entity (the automated hourly job); an x-api-key polls only the calling entity, rate-limited to once per 60 minutes per entity (429 POLL_COOLDOWN_ACTIVE). The shared rate budget for the certificate's CNPJ root may also be parked (RATE_BUDGET_PARKED) or held after repeated errors (RATE_BUDGET_MANUAL_HOLD); a manual trigger never bypasses it and answers 429 (with Retry-After when parked) before any ADN call is attempted. Requires an API key with write access; a read-only key is refused with 403.",
        "tags": [
          "E-Invoicing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Poll cycle result (cron sweep, or this one entity's own outcome for a manual call).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "enabled": {
                      "type": "boolean"
                    },
                    "entitiesPolled": {
                      "type": "integer"
                    },
                    "cycleId": {
                      "type": "string"
                    },
                    "documentFamily": {
                      "type": "string",
                      "enum": [
                        "nfse"
                      ]
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "entityId": {
                            "type": "string"
                          },
                          "newCount": {
                            "type": "integer",
                            "description": "New NFS-e documents stored this cycle."
                          },
                          "credentialInvalid": {
                            "type": "boolean"
                          },
                          "skipped": {
                            "type": "string",
                            "enum": [
                              "not_verified",
                              "probe_passed"
                            ],
                            "description": "Present when the entity was skipped before any ADN call was attempted."
                          },
                          "errors": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key / cron secret",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This API key is read-only — polling ingests invoices into your inbox and requires write access.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "A manual (x-api-key) poll was already triggered for this entity within the last 60 minutes (POLL_COOLDOWN_ACTIVE), or the shared ADN rate budget for this certificate's CNPJ root is parked (RATE_BUDGET_PARKED) or held (RATE_BUDGET_MANUAL_HOLD — an administrator must release the hold).",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds until the cooldown/park clears — absent for RATE_BUDGET_MANUAL_HOLD."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Poll failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/filings": {
      "get": {
        "operationId": "listFilings",
        "summary": "List filings",
        "tags": [
          "Filings"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "view",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "upcoming",
                "filed",
                "backdated"
              ]
            },
            "description": "open = every return not yet filed, backdated ones included, soonest due date first (no due date last); filed = every filed return, backdated or not for live filings (stored filings: go-forward FILED or NOT_FILED only, backdated ones are not included); upcoming = open go-forward returns; backdated = catch-up returns. For live filings, backdated = any return covered by a paid earlier-period order of the same country, upcoming = a not-filed return that is not backdated; open and upcoming are ordered by due date, the others by period, newest first."
          },
          {
            "name": "backdated",
            "in": "query",
            "schema": {
              "type": "boolean"
            },
            "description": "true = only backdated returns, false = only returns that are not backdated. Combined with `view` by AND, so `view=backdated&backdated=false` is always empty."
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "jurisdictionId",
            "in": "query",
            "schema": {
              "type": "string",
              "example": "US_CA"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of filings.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "filings": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/FilingSummary"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "limit": {
                      "type": "integer"
                    },
                    "offset": {
                      "type": "integer"
                    },
                    "live": {
                      "type": "boolean",
                      "description": "Present and true when the list is read live from the filing service on this request."
                    },
                    "emptyState": {
                      "type": "object",
                      "description": "Live list only: wording for an empty list, chosen by the view and backdated filter (for example \"Nothing to approve right now\" for view=open). Render it as is.",
                      "properties": {
                        "title": {
                          "type": "string"
                        },
                        "body": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/FilingValidationError"
          },
          "503": {
            "$ref": "#/components/responses/FilingDataUnavailable"
          }
        },
        "description": "List the entity's tax returns (filings), one per jurisdiction and period, with their status and what you can do next. Needs a key with the `filings` product. Returns are listed newest period first, except `view=open` and `view=upcoming`, which list the soonest due date first. A filing with an id starting `ret_` is read live on each call (`live: true`); the only action on it is approve."
      }
    },
    "/filings/{id}": {
      "get": {
        "operationId": "getFiling",
        "summary": "Get a filing",
        "tags": [
          "Filings"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "$ref": "#/components/parameters/FilingId"
          }
        ],
        "responses": {
          "200": {
            "description": "The filing with totals, authority lines, scope notes and timeline. A live filing (`ret_` id) carries its own fields and `documents` instead of the stored totals, lines and timeline.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "filing": {
                      "$ref": "#/components/schemas/FilingDetail"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/FilingNotFound"
          },
          "503": {
            "$ref": "#/components/responses/FilingDataUnavailable"
          }
        },
        "description": "One filing with its totals, the lines by tax authority, scope notes (what the return includes and excludes) and a timeline. `allowedActions` says which actions are possible now. A live filing (`ret_` id) has its status, period, deadlines, amount and a `documents` list instead; when `allowedActions.approve` is true it also carries `approveConfirmation`, the wording to show before approving."
      }
    },
    "/filings/{id}/documents/{docId}": {
      "get": {
        "operationId": "getFilingDocument",
        "summary": "Download a document of a live filing",
        "description": "Streams one document listed in `documents` on a live filing (`ret_` id), as an attachment. Nothing is stored; the file is fetched on each call. Any other filing id answers 404.",
        "tags": [
          "Filings"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "$ref": "#/components/parameters/FilingId"
          },
          {
            "name": "docId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The file, as an attachment.",
            "content": {
              "application/pdf": {},
              "application/octet-stream": {}
            }
          },
          "404": {
            "$ref": "#/components/responses/FilingNotFound"
          },
          "503": {
            "$ref": "#/components/responses/FilingDataUnavailable"
          }
        }
      }
    },
    "/filings/{id}/approve": {
      "post": {
        "operationId": "approveFiling",
        "summary": "Approve a filing for submission",
        "description": "Needs a write key. For a live filing (`ret_` id) this approves the return once. An approval cannot be undone, withdrawn or changed, so `undo`, `skip`, `prepayment` and change requests answer `404 FILING_NOT_FOUND` for a `ret_` id. The request body is optional for a live filing and `snapshotVersion` is ignored: the return itself decides whether it can still be approved. Answers `200` with `ok: true` and the refreshed `filing`; if the return was already approved (including by a concurrent call) the same `200` carries `alreadyApplied: true`. `409 NOT_AWAITING_APPROVAL` when the return is not ready for approval, `409 BANK_DETAILS_REQUIRED` when the bank account the tax authority will debit is missing, `403 FORBIDDEN` for a read-only key, `402 plan_required` without a filing subscription, `503 AUDIT_UNAVAILABLE` when the approval could not be recorded (nothing was approved), `503 FILING_DATA_UNAVAILABLE` when the return cannot be reached right now (retry shortly) and `503 APPROVAL_UNCONFIRMED` when the outcome could not be confirmed (check the return before trying again). `409 APPROVAL_NOT_COMPLETED` means the approval was recorded but the return is not yet approved. The `200` body carries a `message` to show as is. Offer the action only when `allowedActions.approve` is true on the detail, and show `approveConfirmation` first. Every attempt is recorded in an audit log, naming who approved and through which channel.",
        "tags": [
          "Filings"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "$ref": "#/components/parameters/FilingId"
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Required for a stored filing. Approving a live filing (`ret_` id) needs no body, and any `snapshotVersion` sent is ignored.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "snapshotVersion"
                ],
                "properties": {
                  "snapshotVersion": {
                    "type": "integer",
                    "minimum": 1
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/FilingActionApplied"
          },
          "402": {
            "$ref": "#/components/responses/FilingPaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/FilingApproveForbidden"
          },
          "409": {
            "$ref": "#/components/responses/FilingApproveConflict"
          },
          "422": {
            "$ref": "#/components/responses/FilingValidationError"
          },
          "503": {
            "$ref": "#/components/responses/FilingApprovalUnavailable"
          }
        }
      }
    },
    "/filings/{id}/undo": {
      "post": {
        "operationId": "undoFilingApproval",
        "summary": "Undo an approval (before submission)",
        "tags": [
          "Filings"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "$ref": "#/components/parameters/FilingId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "snapshotVersion"
                ],
                "properties": {
                  "snapshotVersion": {
                    "type": "integer",
                    "minimum": 1
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/FilingActionApplied"
          },
          "409": {
            "$ref": "#/components/responses/FilingConflict"
          }
        }
      }
    },
    "/filings/{id}/pause": {
      "post": {
        "operationId": "pauseFiling",
        "summary": "Pause a filing",
        "description": "For a live filing (`ret_` id) this holds back our finalising of a return that is waiting for approval, so the customer has more time to review it. The customer can still approve at any time and can end the pause early with `resume`. The body is optional: `endDate` (yyyy-mm-dd) is the last day of the pause, within `pause.allowedEnd` of the filing (up to 10 days, the exact last allowed date is `allowedEnd.latest`; never after the 15th of the month after the period and never on or after the last day the return can be approved); omit it for the latest allowed day. A return can be paused once, an organisation can pause at most 5 returns in any 12 months, and a backdated return, a return that stays on hold until the customer approves it, and an approved return cannot be paused. Offer the action only when `pause.canPause` is true; when it is false `pause.canPauseReason` says why, in plain words. After a pause ends or is resumed we still wait until the day after its last day before we finalise, so the customer keeps a fair window. Answers `200` with `ok: true`, a `message` to show as is and the refreshed `filing`; a repeat while the pause is open is the same `200` with `alreadyApplied: true`. `409 NOT_AWAITING_APPROVAL`, `409 PAUSE_NOT_ALLOWED`, `409 PAUSE_PAST_CUTOFF`, `409 PAUSE_LIMIT_REACHED`, `422 INVALID_PAUSE_END` (carries `allowedEnd`), `403 FORBIDDEN` for a read-only role or key, `402 plan_required` without a filing subscription, `503 AUDIT_UNAVAILABLE` (nothing was changed) and `503 FILING_DATA_UNAVAILABLE`. Every attempt is recorded in the audit log. The dashboard route requires `Content-Type: application/json` and shares the approval rate limit (`429 RATE_LIMITED`).",
        "tags": [
          "Filings"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "$ref": "#/components/parameters/FilingId"
          }
        ],
        "requestBody": {
          "required": false,
          "description": "A stored filing needs `snapshotVersion`. A live filing (`ret_` id) takes an optional body: `endDate` only; `snapshotVersion` is ignored.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "snapshotVersion": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "Required for a stored filing; ignored for a live filing."
                  },
                  "endDate": {
                    "type": "string",
                    "format": "date",
                    "example": "2026-10-12",
                    "description": "Live filing only: the last day of the pause. Omit for the latest allowed day (see `pause.allowedEnd` on the filing)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/FilingActionApplied"
          },
          "402": {
            "$ref": "#/components/responses/FilingPaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/FilingApproveForbidden"
          },
          "409": {
            "$ref": "#/components/responses/FilingPauseConflict"
          },
          "422": {
            "$ref": "#/components/responses/FilingPauseValidation"
          },
          "503": {
            "$ref": "#/components/responses/FilingApprovalUnavailable"
          }
        }
      }
    },
    "/filings/{id}/resume": {
      "post": {
        "operationId": "resumeFiling",
        "summary": "Resume a paused filing",
        "description": "For a live filing (`ret_` id) this ends an open pause early. No body is needed. Offer the action only when `pause.canResume` is true. We then wait until the day after today before we finalise the return, so the customer keeps a fair window. A repeat is `200` with `alreadyApplied: true`; `409 NOT_PAUSED` when the return is not paused (or its pause already ran out) and `409 NOT_AWAITING_APPROVAL` when it is no longer waiting for approval. The other errors are those of `pause`.",
        "tags": [
          "Filings"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "$ref": "#/components/parameters/FilingId"
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Required for a stored filing. Approving or resuming a live filing (`ret_` id) needs no body, and any `snapshotVersion` sent is ignored.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "snapshotVersion"
                ],
                "properties": {
                  "snapshotVersion": {
                    "type": "integer",
                    "minimum": 1
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/FilingActionApplied"
          },
          "402": {
            "$ref": "#/components/responses/FilingPaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/FilingApproveForbidden"
          },
          "409": {
            "$ref": "#/components/responses/FilingPauseConflict"
          },
          "503": {
            "$ref": "#/components/responses/FilingApprovalUnavailable"
          }
        }
      }
    },
    "/filings/sandbox/generate": {
      "post": {
        "operationId": "generateSandboxFiling",
        "summary": "Build a sandbox filing on demand",
        "description": "Sandbox keys only. Builds the filing for one period of a registration whose filing frequency is set (the same generator the daily job uses, with the built-in sandbox deadlines) and opens it for approval, so the whole lifecycle can be tried without waiting for the daily job. Nothing is emailed, billed or sent to operations; `filing.*` webhooks still fire to sandbox webhook endpoints. Repeating the call for the same period returns the existing filing with `created: false`. A live key gets `409 SANDBOX_ONLY`.",
        "tags": [
          "Filings"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "registrationId"
                ],
                "properties": {
                  "registrationId": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "periodKey": {
                    "type": "string",
                    "example": "2026-09",
                    "description": "Monthly 2026-09 or quarterly 2026-Q3. Omitted: the most recent closed period. A period closes on the day after its last day."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The filing for this period already existed."
          },
          "201": {
            "description": "The filing was built and opened for approval."
          },
          "409": {
            "description": "SANDBOX_ONLY (live key) or FILING_NOT_SET_UP (no filing frequency chosen for the registration)."
          },
          "422": {
            "$ref": "#/components/responses/FilingValidationError"
          }
        }
      }
    },
    "/filings/{id}/skip": {
      "post": {
        "operationId": "skipFiling",
        "summary": "Skip a filing (closes it as NOT_FILED)",
        "tags": [
          "Filings"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "$ref": "#/components/parameters/FilingId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "snapshotVersion",
                  "reason"
                ],
                "properties": {
                  "snapshotVersion": {
                    "type": "integer",
                    "minimum": 1
                  },
                  "reason": {
                    "type": "string",
                    "maxLength": 500
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/FilingActionApplied"
          },
          "409": {
            "$ref": "#/components/responses/FilingConflict"
          },
          "422": {
            "$ref": "#/components/responses/FilingValidationError"
          }
        }
      }
    },
    "/filings/{id}/prepayment": {
      "post": {
        "operationId": "setFilingPrepayment",
        "summary": "Record an amount already paid for the period",
        "tags": [
          "Filings"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "$ref": "#/components/parameters/FilingId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "snapshotVersion",
                  "amount"
                ],
                "properties": {
                  "snapshotVersion": {
                    "type": "integer",
                    "minimum": 1
                  },
                  "amount": {
                    "type": "number",
                    "minimum": 0,
                    "description": "Cannot exceed the tax due before prepayment (422 PREPAYMENT_EXCEEDS_AMOUNT_DUE)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/FilingActionApplied"
          },
          "409": {
            "$ref": "#/components/responses/FilingConflict"
          },
          "422": {
            "$ref": "#/components/responses/FilingValidationError"
          }
        }
      }
    },
    "/filings/{id}/change-requests": {
      "get": {
        "operationId": "listFilingChangeRequests",
        "summary": "List change requests sent to operations",
        "tags": [
          "Filings"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "$ref": "#/components/parameters/FilingId"
          }
        ],
        "responses": {
          "200": {
            "description": "Change requests, oldest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "changeRequests": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "message": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createFilingChangeRequest",
        "summary": "Ask operations to change a filing",
        "tags": [
          "Filings"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "$ref": "#/components/parameters/FilingId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "message"
                ],
                "properties": {
                  "message": {
                    "type": "string",
                    "maxLength": 2000
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The request was recorded."
          },
          "422": {
            "$ref": "#/components/responses/FilingValidationError"
          }
        }
      }
    },
    "/filings/{id}/csv": {
      "get": {
        "operationId": "getFilingSummaryCsv",
        "summary": "Summary by authority (CSV), ending in a TOTAL row",
        "tags": [
          "Filings"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "$ref": "#/components/parameters/FilingId"
          }
        ],
        "responses": {
          "200": {
            "description": "CSV with comment lines stating what the return includes.",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/filings/{id}/transactions.csv": {
      "get": {
        "operationId": "getFilingTransactionsCsv",
        "summary": "The transaction lines frozen into the filing (CSV)",
        "tags": [
          "Filings"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "$ref": "#/components/parameters/FilingId"
          }
        ],
        "responses": {
          "200": {
            "description": "One row per frozen line; no bank details.",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/filings/{id}/proof": {
      "get": {
        "operationId": "getFilingProof",
        "summary": "Proof-of-filing document",
        "tags": [
          "Filings"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "$ref": "#/components/parameters/FilingId"
          }
        ],
        "responses": {
          "200": {
            "description": "The PDF, PNG or JPEG file.",
            "content": {
              "application/pdf": {},
              "image/png": {},
              "image/jpeg": {}
            }
          },
          "404": {
            "description": "No proof has been attached yet (PROOF_NOT_AVAILABLE)."
          }
        }
      }
    },
    "/filings/setups": {
      "get": {
        "operationId": "listFilingSetups",
        "summary": "Where filing setup stands for each registration, and the next step",
        "description": "One row per registration of the entity with status `REGISTERED`. `state` is the filing setup state (`NOT_SET_UP`, `REQUESTED`, `ENABLED`, `STOPPED_AFTER`). `nextStep` is `SET_UP`, `ADD_PAYMENT_ACCOUNT`, `WAIT_FOR_ONBOARDING`, `RESTART` or `NONE`; `nextStepLabel`, `stateLabel` and `actionLabel` are rendered as is. Registrations that cannot file (a local registration, or one we cannot file yet) are listed with `eligible: false`, `nextStep: NONE` and the reason in `ineligibleReason`; while filing data is temporarily unavailable the reason says so. `ineligibleReason` is display text in this list; on `GET /tax/registrations/{id}/filing` it is the enum `locality` | `country` or null. Programme registrations (such as CO_SUTS) and local rows without a locality report the setup of the state's standard registration. Needing action first, then active, then the rest. `filingAccess` (once, not per row) says whether the account may set up filing now; `canStart` on the setup covers eligibility and the bank account, `filingAccess.canSetUp` covers the subscription. Use `GET /tax/registrations/{id}/filing` for the full setup of one registration.",
        "tags": [
          "Filings"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "The setups.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "filingAccess": {
                      "$ref": "#/components/schemas/FilingAccess"
                    },
                    "setups": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/FilingSetupSummary"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/filings/bank-account": {
      "get": {
        "operationId": "getFilingBankAccount",
        "summary": "The account the tax authority debits (last four digits only)",
        "tags": [
          "Filings"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "The active bank account or null.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "bankAccount": {
                      "nullable": true,
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "holderName": {
                          "type": "string"
                        },
                        "routingLast4": {
                          "type": "string"
                        },
                        "accountLast4": {
                          "type": "string"
                        },
                        "accountType": {
                          "type": "string",
                          "nullable": true
                        },
                        "country": {
                          "type": "string"
                        },
                        "currency": {
                          "type": "string"
                        },
                        "verifiedAt": {
                          "type": "string",
                          "format": "date-time",
                          "nullable": true
                        },
                        "createdAt": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/filings/upload/months": {
      "get": {
        "operationId": "listFilingUploadMonths",
        "summary": "The months you can add data to, with their window state",
        "description": "The months you can add data to, newest first, up to 36. `state` and `window` say whether a month is open for data. A month opens when its period closes (the day after its last day) and stays open until the end of the day on which the return is approved automatically, which is the 5th of the following month by default; an earlier-period month stays open until a submitted or approved return locks it. There is no call to close a month: it is finished by approving the return. The `closed` state exists only on older months and is read-only. `linked` is false until the business profile is set up for filing. A read key is enough.",
        "tags": [
          "Filing Data Upload"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          }
        ],
        "responses": {
          "200": {
            "description": "The months, newest first (up to 36).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FilingUploadMonthList"
                }
              }
            }
          },
          "402": {
            "$ref": "#/components/responses/FilingPaymentRequired"
          },
          "503": {
            "$ref": "#/components/responses/FilingUploadUnavailable"
          }
        }
      }
    },
    "/filings/upload/months/{month}": {
      "get": {
        "operationId": "getFilingUploadMonth",
        "summary": "Files, counts, window and lock for one month",
        "tags": [
          "Filing Data Upload"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "$ref": "#/components/parameters/FilingUploadMonth"
          }
        ],
        "responses": {
          "200": {
            "description": "The month with its files, counts, window and declaration.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FilingUploadMonthResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/FilingValidationError"
          },
          "404": {
            "description": "No such month (`month_not_found`)."
          },
          "503": {
            "$ref": "#/components/responses/FilingUploadUnavailable"
          }
        },
        "description": "One month: its files (including the file Clearvo calculates for you), counts, the upload window, the declaration to accept, `canUpload` / `canConfirm` and `allowedActions` (`upload`, `delete`, `process`). While any file has errors, `canConfirm` and `allowedActions.process` are false and `message` says what to do (with `contactUrl` for a file prepared for you). Counts exist once a file has been processed."
      }
    },
    "/filings/upload/months/{month}/files": {
      "post": {
        "operationId": "uploadFilingDataFile",
        "summary": "Add a data file to a month",
        "description": "Add a data file to the month. Send multipart form data with the file in the field `file` (.csv, .xlsx or .xls, up to 25 MB) and a `Content-Length` header. Use as many files as you like per month: every file in the month is processed together. Needs a write key and a month that is open (`MONTH_NOT_OPEN`, `MONTH_CLOSED`, `MONTH_LOCKED`, `PERIOD_PROCESSING` otherwise). A file with the same name or the same content as one already added is `409 FILE_ALREADY_ADDED` (carries `existingId` and `reason`); to replace a file, delete it and upload the new one. Files are checked when the month is processed, not on upload.",
        "tags": [
          "Filing Data Upload"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "$ref": "#/components/parameters/FilingUploadMonth"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The file was added. It stays \"Not processed yet\" until the month is processed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FilingUploadFileResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/FilingValidationError"
          },
          "402": {
            "$ref": "#/components/responses/FilingPaymentRequired"
          },
          "409": {
            "$ref": "#/components/responses/FilingUploadConflict"
          },
          "411": {
            "description": "`LENGTH_REQUIRED`."
          },
          "413": {
            "description": "`FILE_TOO_LARGE` (carries `maxBytes`)."
          },
          "415": {
            "description": "`UNSUPPORTED_FILE_TYPE` (carries `allowed`)."
          },
          "503": {
            "$ref": "#/components/responses/FilingUploadUnavailable"
          }
        }
      }
    },
    "/filings/upload/months/{month}/files/{fileId}": {
      "delete": {
        "operationId": "deleteFilingDataFile",
        "summary": "Remove a file you added, while the month is open",
        "description": "Remove a file you added while the month is open. Needs a write key. The file Clearvo calculates for you cannot be removed (`409 SYSTEM_FILE_NOT_DELETABLE`). After a change, the month must be processed again.",
        "tags": [
          "Filing Data Upload"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "$ref": "#/components/parameters/FilingUploadMonth"
          },
          {
            "$ref": "#/components/parameters/FilingUploadFileId"
          }
        ],
        "responses": {
          "200": {
            "description": "The file was removed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FilingUploadDeleted"
                }
              }
            }
          },
          "402": {
            "$ref": "#/components/responses/FilingPaymentRequired"
          },
          "404": {
            "description": "No such month or file (`month_not_found`, `file_not_found`)."
          },
          "409": {
            "$ref": "#/components/responses/FilingUploadConflict"
          },
          "503": {
            "$ref": "#/components/responses/FilingUploadUnavailable"
          }
        }
      }
    },
    "/filings/upload/months/{month}/files/{fileId}/error-report": {
      "get": {
        "operationId": "getFilingUploadErrorReport",
        "summary": "A fresh download link for the error and warning report of a file",
        "description": "A fresh download link (`url`, https only) for the error and warning report of a processed file. It is available when the file has errors or warnings. The link is returned to you only, is new on every call and should not be stored. `404 no_error_report` when the file has no errors and no warnings.",
        "tags": [
          "Filing Data Upload"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "$ref": "#/components/parameters/FilingUploadMonth"
          },
          {
            "$ref": "#/components/parameters/FilingUploadFileId"
          }
        ],
        "responses": {
          "200": {
            "description": "A fresh download link for the error and warning report.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FilingUploadErrorReport"
                }
              }
            }
          },
          "404": {
            "description": "No error or warning report (`no_error_report`), or no such month or file (`month_not_found`, `file_not_found`)."
          },
          "503": {
            "$ref": "#/components/responses/FilingUploadUnavailable"
          }
        }
      }
    },
    "/filings/upload/months/{month}/process": {
      "post": {
        "operationId": "processFilingUploadMonth",
        "summary": "Confirm all data is added and process the month once",
        "description": "Confirms that you have added all data for the month and processes it once. Needs a write key. The body is only `declarationAccepted: true` and the `declarationVersion` shown with the month (`month.declaration.version`; the wording is `month.declaration.text`); any other field is refused with `400 invalid_body`. Missing or false acceptance is `422 DECLARATION_REQUIRED`; a version that is no longer current is `422 DECLARATION_VERSION_MISMATCH` and the response carries the current `declaration`. The acceptance is recorded with who, when, the version, the files and their counts. If any row in a file has an error, the whole file is not used. While any file in the month has errors the call is refused with `409 FILE_ERRORS` (carries `message` and `filesWithErrors`): fetch the error and warning report of each file, fix the file and upload it again, or delete it, then confirm. If the file with errors is one prepared for you, the call is `409 SYSTEM_FILE_ERRORS` (`message`, `filesWithErrors`, `contactUrl`) instead. Warnings never block. Returns `202` while processing runs. `409 SYSTEM_FILE_PENDING` means the month's calculated transactions are still being added: try again shortly. Repeating the call when nothing has changed since the last clean run returns `200` with `alreadyApplied: true`. If you change files afterwards, process the month again.",
        "tags": [
          "Filing Data Upload"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "$ref": "#/components/parameters/FilingUploadMonth"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "declarationAccepted",
                  "declarationVersion"
                ],
                "properties": {
                  "declarationAccepted": {
                    "type": "boolean",
                    "description": "Must be true."
                  },
                  "declarationVersion": {
                    "type": "string",
                    "description": "The version of the declaration text shown with the month."
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The month was already processed and nothing has changed since (`alreadyApplied: true`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FilingUploadMonthResponse"
                }
              }
            }
          },
          "202": {
            "description": "Accepted. Processing runs in the background: poll `GET /filings/upload/months/{month}` until `state` is `done` or `errors`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FilingUploadMonthResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/FilingValidationError"
          },
          "402": {
            "$ref": "#/components/responses/FilingPaymentRequired"
          },
          "422": {
            "description": "`DECLARATION_REQUIRED` (not accepted) or `DECLARATION_VERSION_MISMATCH` (out of date; carries `currentVersion` and the current `declaration`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FilingUploadDeclarationError"
                }
              }
            }
          },
          "409": {
            "$ref": "#/components/responses/FilingUploadConflict"
          },
          "503": {
            "$ref": "#/components/responses/FilingUploadUnavailable"
          }
        }
      }
    },
    "/filings/upload/template": {
      "get": {
        "operationId": "getFilingUploadTemplate",
        "summary": "The upload template (CSV, XLSX or JSON)",
        "description": "The upload template, one definition in three formats: `csv` (the header row and an example row), `xlsx` (with a field guide sheet) and `json` (columns, formats and rules). Read key is enough.",
        "tags": [
          "Filing Data Upload"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "format",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "csv",
                "xlsx",
                "json"
              ],
              "default": "csv"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The template in the requested format.",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              },
              "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FilingUploadTemplate"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/FilingValidationError"
          }
        }
      }
    },
    "/tax/registrations/{id}/filing": {
      "get": {
        "operationId": "getRegistrationFilingSetup",
        "summary": "The filing setup for a registration",
        "description": "State (`NOT_SET_UP`, `REQUESTED`, `ENABLED`, `STOPPED_AFTER`), the frequency options, the start periods on offer for each frequency, the stored start choice, the bank requirement and `canStart` / `startBlockedReason`. The display text (`frequencyQuestion`, `frequencyHelp`, `frequencyWarning`, `frequencyAttestationLabel`, `startQuestion`) is rendered as is. `canStart` covers the account (eligibility, bank account); the start choice is checked when the request is made. `filingSetup.filingAccess` says whether the account may set up filing now (a subscription is required once billing is live; sandbox is exempt). When filing data is temporarily unavailable the body has `eligible: false`, a null `ineligibleReason` and `startBlockedReason` says so; a lookup that fails part-way returns `503 FILING_DATA_UNAVAILABLE`.",
        "tags": [
          "Filings"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The registration id (from `GET /tax/registrations`)."
          }
        ],
        "responses": {
          "200": {
            "description": "The filing setup.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "filingSetup": {
                      "$ref": "#/components/schemas/FilingSetup"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "REGISTRATION_NOT_FOUND."
          },
          "503": {
            "description": "`FILING_DATA_UNAVAILABLE`: filing data is temporarily unavailable. Try again shortly.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FilingError"
                }
              }
            }
          }
        }
      },
      "put": {
        "operationId": "updateRegistrationFilingSetup",
        "summary": "Request, defer or stop filing for a registration",
        "description": "Send exactly one of the three bodies. Requesting filing needs `frequencyCode` (one of `frequencyOptions[].code`), `frequencyAttested: true` and `startChoice`; there is no default start. `later` also needs `startPeriodKey`, one of the keys in `startOptionsByFrequency[frequencyCode].later`. Errors: `422 START_CHOICE_REQUIRED`, `START_PERIOD_REQUIRED`, `INVALID_START_PERIOD`, `FREQUENCY_NOT_OFFERED`, `FREQUENCY_ATTESTATION_REQUIRED`, `BANK_ACCOUNT_REQUIRED`, `FILING_NOT_AVAILABLE_LOCALITY` and `FILING_NOT_AVAILABLE_JURISDICTION` (filing is not available for this registration yet); each carries `error`, `field` and `detail`. `503 FILING_DATA_UNAVAILABLE` means filing data is temporarily unavailable: try again shortly, it says nothing about whether the registration is supported. Requesting (or replacing) a setup without a subscription is refused with `402 plan_required` (`reason: filing_requires_subscription`); `message` is the same sentence as `filingAccess.message`. Deferring the first period and stopping filing are never refused for this reason. Filing starts only after Clearvo has completed onboarding for the registration (state `REQUESTED` until then).",
        "tags": [
          "Filings"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/EntityId"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The registration id (from `GET /tax/registrations`)."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "type": "object",
                    "required": [
                      "frequencyCode",
                      "frequencyAttested",
                      "startChoice"
                    ],
                    "properties": {
                      "frequencyCode": {
                        "type": "string",
                        "example": "Monthly"
                      },
                      "frequencyAttested": {
                        "type": "boolean"
                      },
                      "startChoice": {
                        "type": "string",
                        "enum": [
                          "asap",
                          "later"
                        ],
                        "description": "asap = the first available period; later = a period you name in `startPeriodKey`."
                      },
                      "startPeriodKey": {
                        "type": "string",
                        "example": "2026-12"
                      }
                    }
                  },
                  {
                    "type": "object",
                    "required": [
                      "deferFirstPeriodTo"
                    ],
                    "properties": {
                      "deferFirstPeriodTo": {
                        "type": "string",
                        "format": "date"
                      }
                    }
                  },
                  {
                    "type": "object",
                    "required": [
                      "stop"
                    ],
                    "properties": {
                      "stop": {
                        "type": "boolean",
                        "enum": [
                          true
                        ]
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated filing setup.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "filingSetup": {
                      "$ref": "#/components/schemas/FilingSetup"
                    }
                  }
                }
              }
            }
          },
          "402": {
            "$ref": "#/components/responses/FilingPaymentRequired"
          },
          "422": {
            "description": "A setup error (`error`, `field`, `detail`), or VALIDATION_FAILED when the body is not one of the three shapes."
          },
          "503": {
            "description": "`FILING_DATA_UNAVAILABLE`: filing data is temporarily unavailable. Try again shortly.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FilingError"
                }
              }
            }
          }
        }
      }
    }
  },
  "tags": [
    {
      "name": "E-Invoicing",
      "description": "Submit, poll, and manage B2B invoices across 37 countries."
    },
    {
      "name": "Customers",
      "description": "Manage your entity's customer master data (name, tax ID, address). Referenced via customer.customerRef on POST /send, and auto-populated from every invoice you issue."
    },
    {
      "name": "Suppliers",
      "description": "Manage your entity's supplier master data (name, tax ID, address) — the vendor side of a purchase. Referenced via supplier.ref on a purchase-direction POST /tax/calculate, and linked automatically from every inbound e-invoice you receive."
    },
    {
      "name": "Company Codes",
      "description": "Enterprise only. An entity's internal split of transactions (an SAP company code, a Coupa business unit): named on POST /tax/calculate and POST /send as companyCode, usable as a rule scope and an Explore filter. Never a separate legal entity: registrations, filings and e-invoicing identity stay on the entity."
    },
    {
      "name": "Bank Accounts",
      "description": "Manage your entity's bank-account master data (IBAN/BIC), one per currency plus an optional entity-wide DEFAULT. Used as a fallback for payment.means[].iban/bic on POST /send when you don't resend them on that specific call."
    },
    {
      "name": "Webhooks",
      "description": "Receive real-time event notifications for invoice status changes."
    },
    {
      "name": "Integration Tests",
      "description": "Run your integration tests in the sandbox, read the outcome, and add tests of your own. Sandbox keys only. Results show how your integration handled simulated scenarios; nothing is sent to any tax authority, network or supplier, and this is not a certification."
    },
    {
      "name": "Peppol",
      "description": "Peppol network participant lookup and AS4 delivery."
    },
    {
      "name": "TIN Validation",
      "description": "Validate VAT numbers and national tax IDs across 100+ countries."
    },
    {
      "name": "Tax Registrations",
      "description": "Record and manage per-jurisdiction tax registrations and collection status."
    },
    {
      "name": "Client Tax Codes",
      "description": "Map your own ERP tax code (e.g. a SAP two-digit code) to a Clearvo treatment — an input to POST /send and an output on POST /tax/calculate."
    },
    {
      "name": "Tax Reporting",
      "description": "Enable or disable periodic tax reporting obligations (France e-reporting, Spain SII, Portugal invoice-data communication to AT) per entity, and choose how each is submitted (immediately, batched and auto-sent, or batched for your review)."
    },
    {
      "name": "Reporting Batches",
      "description": "Spain SII reporting batches — the groups of registros an entity on es_sii submissionMode batch_auto/batch_review accumulates before they go to AEAT. List and inspect batches, confirm a batch that is ready for review, exclude a record, and (sandbox only) run the daily sweeps on demand."
    },
    {
      "name": "Exemption Certificates",
      "description": "Manage customer exemption certificates and apply them to tax calculations."
    },
    {
      "name": "Tax Calculations",
      "description": "Real-time tax calculation, jurisdiction resolution, product classification, obligation monitoring, and account settings."
    },
    {
      "name": "Entities",
      "description": "Manage the fiscal entities (subsidiaries, branches) under your Clearvo account, and look up per-country e-invoicing requirements."
    },
    {
      "name": "Authority Credentials",
      "description": "Register per-country authority-portal credentials (e.g. Poland KSeF, Argentina AFIP, Hungary NAV) required before submitting invoices to that country. France is the exception: no secret to store, just the VAT number that gates the platform's own French connection."
    },
    {
      "name": "Integrations",
      "description": "Connect and sync third-party sales channels and ERPs (e.g. WooCommerce)."
    },
    {
      "name": "Team",
      "description": "Invite and manage teammates on your Clearvo account."
    },
    {
      "name": "Setup",
      "description": "Check account onboarding/setup progress — built for AI agents driving setup end-to-end."
    },
    {
      "name": "Data Query Tool",
      "description": "Ad-hoc filtered, paginated queries and exports over your e-invoicing and tax-calculation data."
    },
    {
      "name": "Rules Engine",
      "description": "Customer-configurable field-mapping and business-rule engine (Global/Tenant/Organisation/Entity scoped) that runs before and after tax calculation — normalization, enrichment, and adjustment rules against AP (purchase) and AR (sale/e-invoicing) records."
    },
    {
      "name": "Filings",
      "description": "Tax returns Clearvo prepares and files for you, for any country or scheme the filing service supports (US states are one example): set up filing for a registration, review and approve each return, and download its documents. A key needs the `filings` product."
    },
    {
      "name": "Filing Data Upload",
      "description": "Add your own transaction data to a filing month: one or more files per month, then confirm and process the month once. A key needs the `filings` product."
    }
  ]
}
