{
  "auth": {
    "bearer": [
      {
        "key": "token",
        "type": "string",
        "value": "{{IRONFANG_API_KEY}}"
      }
    ],
    "type": "bearer"
  },
  "info": {
    "description": "Financewolf validates UBL e-invoices against the exact EN 16931 and Peppol\nBIS Billing rule releases published by the standards bodies, and returns a\nresult that names the immutable ruleset it used so the outcome can be\nreproduced later.\n\nThe production path is the Financewolf Go API/generator, a self-hosted Java\nservice, PHIVE with upstream UBL / EN 16931 / Peppol artefacts, and Saxon.\nXML well-formedness, UBL schema, EN 16931 and Peppol findings remain separate\nlayers. Generated XML is validated as the exact bytes returned to the caller.\nGET /v1/einvoices/rulesets identifies releases and, where available, observed\nruntime dependency versions. Library versions are not specification versions.\n\nValidation does not transmit invoices, make Ironfang a Peppol Access Point,\nregister participants, certify legal or tax compliance, or guarantee recipient\nacceptance. Uploaded documents are not silently corrected.\n\n**Authentication.** The V1 boundary accepts Ironfang platform API keys\nonly (`Authorization: Bearer \u003ckey\u003e`), minted in the Ironfang portal with\nFinancewolf scopes. There is no OAuth or user-token access to this API\nyet; nothing in this contract should be read as advertising one.\n\n**Anonymous use.** Validation, generation, report verification, public signing keys and both ruleset reads\naccept requests with no credentials. Anonymous validation is validation\nonly: no operation or document is stored, no report or evidence link is\nproduced, no usage or entitlement information is returned, and tighter\nper-address rate limits apply. The result shape is otherwise identical.\n\n**Saved results.** Authenticated response bytes and findings are kept for\n30 days and may be deleted earlier through the result API. Synchronous validation\ndoes not store uploaded XML. Durable jobs temporarily retain encrypted XML or generation\nJSON until completion, cancellation or 24-hour expiry; cleanup runs each minute.\nExpired payloads are never executed. Database backups follow their own retention policy. Authenticated generation retains the exact generated XML in its\nresult. POST /v1/einvoices/render reads that result to produce a readable PDF\nwithout another usage charge; the structured XML remains authoritative. After expiry/deletion, replay under the same Idempotency-Key\nanswers `410 result_gone` without running or charging again. Operation\nidentity hashes and usage metadata remain until organisation erasure.\nExisting results receive 30 days from the retention migration.\n\n**Durable jobs.** Authenticated POST /v1/einvoices/jobs and /batches accept\nvalidation or generation work, returning 202 metadata after atomic persistence.\nPoll the job/batch; fetch completed response bytes using its operation_id through\n/results/{id}. Idempotency-Key replays return 200 current metadata, including after\nresult deletion, without rerunning work. This differs from synchronous replay.\nJob and batch key scopes are independent. Pending usage_charged=false describes\ncurrent usage; a completed billable validation or generation adds one ledger event.\nNo HTTP request cancellation implicitly cancels an accepted job. POST /{id}/cancel\nis explicit and has no body. It preserves completed children of a batch.\n\n**Signed reports.** Report issuance reads a retained result with complete findings\nand requires a configured signer. The signature binds observations and exact bytes;\nreport integrity is separate from invoice validity. Verification needs no account,\nstores no uploads and contacts no external services. Signing keys are published\nseparately. An offline CLI supports independently obtained keys and TSA CA roots.\n\n**Validation is not transport.** Any `scheduled`, `sendable` or\n`historical` release may be validated against by exact id: historical\nfor audit and reproduction, scheduled for migration testing. Only the\nkeyword `latest` (and profile detection) is restricted to the currently\n`sendable` release. Sending a document over Peppol - a later slice -\nis what requires a sendable release; validation never does.\n\n**Indeterminate outcomes.** A timeout, validator crash or internal\ndependency failure is *indeterminate*: it is never reported as `invalid`\nand never billed as a completed validation. Indeterminate outcomes are\ndelivered as Problems - `504 validation_timeout`,\n`503 validator_unavailable` or `500 internal_error` on the validate\noperation - each carrying `outcome: indeterminate` and\n`usage_charged: false`. A `200` validation response is a verdict:\n`status: completed` with `outcome` `valid` or `invalid`, and no layer in\n`error`; the response schema (`ValidationVerdict`) admits nothing else.\n\nGenerated from info.version 1.4. Start with the Getting started folder: its requests need no credential. For everything else set IRONFANG_API_KEY in a private Postman environment or vault reference; no credential is included in this collection. Supply path variables before sending. DELETE permanently deletes a retained result. Generation requires generation-input/v1 JSON in generation_input. Do not export credentials.",
    "name": "Financewolf E-Invoicing API",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "item": [
    {
      "description": "Three anonymous requests that work from a fresh fork with nothing configured. Run them in order. Nothing here needs an API key; the folders below do, except where a request says otherwise.",
      "item": [
        {
          "name": "1. List the current rulesets",
          "request": {
            "auth": {
              "type": "noauth"
            },
            "description": "Lists the rule releases Financewolf validates against, newest effective date first. Each has an immutable id. Send this first: it needs no credential and proves the base URL and network path. The entry marked latest for your document type is what `ruleset=latest` resolves to.",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "einvoices",
                "rulesets"
              ],
              "query": [],
              "raw": "{{base_url}}/v1/einvoices/rulesets"
            }
          },
          "response": []
        },
        {
          "name": "2. Validate a sample invoice",
          "request": {
            "auth": {
              "type": "noauth"
            },
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "xml"
                }
              },
              "raw": "\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003cInvoice xmlns=\"urn:oasis:names:specification:ubl:schema:xsd:Invoice-2\" xmlns:cac=\"urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2\" xmlns:cbc=\"urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2\"\u003e\n  \u003ccbc:CustomizationID\u003eurn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0\u003c/cbc:CustomizationID\u003e\n  \u003ccbc:ProfileID\u003eurn:fdc:peppol.eu:2017:poacc:billing:01:1.0\u003c/cbc:ProfileID\u003e\n  \u003ccbc:ID\u003eEXAMPLE-INV-001\u003c/cbc:ID\u003e\n  \u003ccbc:IssueDate\u003e2026-09-08\u003c/cbc:IssueDate\u003e\n  \u003ccbc:DueDate\u003e2026-10-08\u003c/cbc:DueDate\u003e\n  \u003ccbc:InvoiceTypeCode\u003e380\u003c/cbc:InvoiceTypeCode\u003e\n  \u003ccbc:DocumentCurrencyCode\u003eGBP\u003c/cbc:DocumentCurrencyCode\u003e\n  \u003ccbc:BuyerReference\u003eBUYER-REF-001\u003c/cbc:BuyerReference\u003e\n  \u003ccac:AccountingSupplierParty\u003e\n    \u003ccac:Party\u003e\n      \u003ccbc:EndpointID schemeID=\"0088\"\u003e7300010000001\u003c/cbc:EndpointID\u003e\n      \u003ccac:PostalAddress\u003e\n        \u003ccbc:StreetName\u003e1 Example Street\u003c/cbc:StreetName\u003e\n        \u003ccbc:CityName\u003eLondon\u003c/cbc:CityName\u003e\n        \u003ccbc:PostalZone\u003eSW1A 1AA\u003c/cbc:PostalZone\u003e\n        \u003ccac:Country\u003e\n          \u003ccbc:IdentificationCode\u003eGB\u003c/cbc:IdentificationCode\u003e\n        \u003c/cac:Country\u003e\n      \u003c/cac:PostalAddress\u003e\n      \u003ccac:PartyTaxScheme\u003e\n        \u003ccbc:CompanyID\u003eGB123456789\u003c/cbc:CompanyID\u003e\n        \u003ccac:TaxScheme\u003e\n          \u003ccbc:ID\u003eVAT\u003c/cbc:ID\u003e\n        \u003c/cac:TaxScheme\u003e\n      \u003c/cac:PartyTaxScheme\u003e\n      \u003ccac:PartyLegalEntity\u003e\n        \u003ccbc:RegistrationName\u003eExample Supplier Ltd\u003c/cbc:RegistrationName\u003e\n        \u003ccbc:CompanyID\u003e12345678\u003c/cbc:CompanyID\u003e\n      \u003c/cac:PartyLegalEntity\u003e\n    \u003c/cac:Party\u003e\n  \u003c/cac:AccountingSupplierParty\u003e\n  \u003ccac:AccountingCustomerParty\u003e\n    \u003ccac:Party\u003e\n      \u003ccbc:EndpointID schemeID=\"0088\"\u003e7300010000001\u003c/cbc:EndpointID\u003e\n      \u003ccac:PostalAddress\u003e\n        \u003ccbc:StreetName\u003e2 Example Street\u003c/cbc:StreetName\u003e\n        \u003ccbc:CityName\u003eLondon\u003c/cbc:CityName\u003e\n        \u003ccbc:PostalZone\u003eSW1A 2AA\u003c/cbc:PostalZone\u003e\n        \u003ccac:Country\u003e\n          \u003ccbc:IdentificationCode\u003eGB\u003c/cbc:IdentificationCode\u003e\n        \u003c/cac:Country\u003e\n      \u003c/cac:PostalAddress\u003e\n      \u003ccac:PartyLegalEntity\u003e\n        \u003ccbc:RegistrationName\u003eExample Buyer Ltd\u003c/cbc:RegistrationName\u003e\n        \u003ccbc:CompanyID\u003e87654321\u003c/cbc:CompanyID\u003e\n      \u003c/cac:PartyLegalEntity\u003e\n    \u003c/cac:Party\u003e\n  \u003c/cac:AccountingCustomerParty\u003e\n  \u003ccac:TaxTotal\u003e\n    \u003ccbc:TaxAmount currencyID=\"GBP\"\u003e5.00\u003c/cbc:TaxAmount\u003e\n    \u003ccac:TaxSubtotal\u003e\n      \u003ccbc:TaxableAmount currencyID=\"GBP\"\u003e25.00\u003c/cbc:TaxableAmount\u003e\n      \u003ccbc:TaxAmount currencyID=\"GBP\"\u003e5.00\u003c/cbc:TaxAmount\u003e\n      \u003ccac:TaxCategory\u003e\n        \u003ccbc:ID\u003eS\u003c/cbc:ID\u003e\n        \u003ccbc:Percent\u003e20\u003c/cbc:Percent\u003e\n        \u003ccac:TaxScheme\u003e\n          \u003ccbc:ID\u003eVAT\u003c/cbc:ID\u003e\n        \u003c/cac:TaxScheme\u003e\n      \u003c/cac:TaxCategory\u003e\n    \u003c/cac:TaxSubtotal\u003e\n  \u003c/cac:TaxTotal\u003e\n  \u003ccac:LegalMonetaryTotal\u003e\n    \u003ccbc:LineExtensionAmount currencyID=\"GBP\"\u003e25.00\u003c/cbc:LineExtensionAmount\u003e\n    \u003ccbc:TaxExclusiveAmount currencyID=\"GBP\"\u003e25.00\u003c/cbc:TaxExclusiveAmount\u003e\n    \u003ccbc:TaxInclusiveAmount currencyID=\"GBP\"\u003e30.00\u003c/cbc:TaxInclusiveAmount\u003e\n    \u003ccbc:AllowanceTotalAmount currencyID=\"GBP\"\u003e0.00\u003c/cbc:AllowanceTotalAmount\u003e\n    \u003ccbc:ChargeTotalAmount currencyID=\"GBP\"\u003e0.00\u003c/cbc:ChargeTotalAmount\u003e\n    \u003ccbc:PrepaidAmount currencyID=\"GBP\"\u003e0.00\u003c/cbc:PrepaidAmount\u003e\n    \u003ccbc:PayableRoundingAmount currencyID=\"GBP\"\u003e0.00\u003c/cbc:PayableRoundingAmount\u003e\n    \u003ccbc:PayableAmount currencyID=\"GBP\"\u003e30.00\u003c/cbc:PayableAmount\u003e\n  \u003c/cac:LegalMonetaryTotal\u003e\n  \u003ccac:InvoiceLine\u003e\n    \u003ccbc:ID\u003e1\u003c/cbc:ID\u003e\n    \u003ccbc:InvoicedQuantity unitCode=\"C62\"\u003e2\u003c/cbc:InvoicedQuantity\u003e\n    \u003ccbc:LineExtensionAmount currencyID=\"GBP\"\u003e25.00\u003c/cbc:LineExtensionAmount\u003e\n    \u003ccac:Item\u003e\n      \u003ccbc:Name\u003eExample service\u003c/cbc:Name\u003e\n      \u003ccac:ClassifiedTaxCategory\u003e\n        \u003ccbc:ID\u003eS\u003c/cbc:ID\u003e\n        \u003ccbc:Percent\u003e20\u003c/cbc:Percent\u003e\n        \u003ccac:TaxScheme\u003e\n          \u003ccbc:ID\u003eVAT\u003c/cbc:ID\u003e\n        \u003c/cac:TaxScheme\u003e\n      \u003c/cac:ClassifiedTaxCategory\u003e\n    \u003c/cac:Item\u003e\n    \u003ccac:Price\u003e\n      \u003ccbc:PriceAmount currencyID=\"GBP\"\u003e12.5\u003c/cbc:PriceAmount\u003e\n      \u003ccbc:BaseQuantity unitCode=\"C62\"\u003e1\u003c/cbc:BaseQuantity\u003e\n    \u003c/cac:Price\u003e\n  \u003c/cac:InvoiceLine\u003e\n\u003c/Invoice\u003e\n"
            },
            "description": "Validates the UBL invoice in the body against the latest Peppol BIS Billing 3 ruleset and returns a verdict with findings per layer. The body is a conformant sample, so expect `outcome: valid`. Replace it with your own Invoice or CreditNote XML to see real findings. Anonymous validation stores nothing and produces no report; set IRONFANG_API_KEY in a private environment to keep results.",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/xml"
              }
            ],
            "method": "POST",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "einvoices",
                "validate"
              ],
              "query": [],
              "raw": "{{base_url}}/v1/einvoices/validate"
            }
          },
          "response": []
        },
        {
          "name": "3. Generate an invoice from JSON",
          "request": {
            "auth": {
              "type": "noauth"
            },
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\n  \"input_schema_version\": \"financewolf/einvoice/generation-input/v1\",\n  \"document_type\": \"invoice\",\n  \"id\": \"EXAMPLE-INV-001\",\n  \"issue_date\": \"2026-09-08\",\n  \"due_date\": \"2026-10-08\",\n  \"currency\": \"GBP\",\n  \"buyer_reference\": \"BUYER-REF-001\",\n  \"seller\": {\n    \"name\": \"Example Supplier Ltd\",\n    \"endpoint\": {\n      \"scheme\": \"0088\",\n      \"value\": \"7300010000001\"\n    },\n    \"legal_identifier\": {\n      \"value\": \"12345678\"\n    },\n    \"vat_identifier\": \"GB123456789\",\n    \"address\": {\n      \"street\": \"1 Example Street\",\n      \"city\": \"London\",\n      \"postal_code\": \"SW1A 1AA\",\n      \"country\": \"GB\"\n    }\n  },\n  \"buyer\": {\n    \"name\": \"Example Buyer Ltd\",\n    \"endpoint\": {\n      \"scheme\": \"0088\",\n      \"value\": \"7300010000001\"\n    },\n    \"legal_identifier\": {\n      \"value\": \"87654321\"\n    },\n    \"address\": {\n      \"street\": \"2 Example Street\",\n      \"city\": \"London\",\n      \"postal_code\": \"SW1A 2AA\",\n      \"country\": \"GB\"\n    }\n  },\n  \"lines\": [\n    {\n      \"id\": \"1\",\n      \"quantity\": \"2\",\n      \"unit\": \"C62\",\n      \"price_amount\": \"12.50\",\n      \"item\": {\n        \"name\": \"Example service\"\n      },\n      \"tax\": {\n        \"category\": \"S\",\n        \"rate\": \"20\"\n      }\n    }\n  ]\n}"
            },
            "description": "Builds a UBL invoice from the generation-input/v1 JSON in the body, validates it against the selected ruleset, and returns the XML base64-encoded in `artifact.data_base64` together with the validation verdict. The body is the generator's minimal example; the XML it returns is the same document the previous request validates.",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "method": "POST",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "einvoices",
                "generate"
              ],
              "query": [
                {
                  "key": "ruleset",
                  "value": "{{ruleset}}"
                },
                {
                  "key": "profile",
                  "value": "{{profile}}"
                }
              ],
              "raw": "{{base_url}}/v1/einvoices/generate?ruleset={{ruleset}}\u0026profile={{profile}}"
            }
          },
          "response": []
        }
      ],
      "name": "Getting started"
    },
    {
      "description": "PHIVE validation of UBL Invoice and CreditNote XML with structured findings.",
      "item": [
        {
          "name": "Validate one e-invoice XML document",
          "request": {
            "body": {
              "file": {
                "src": ""
              },
              "mode": "file"
            },
            "description": "Validates a single UBL Invoice or CreditNote document synchronously and\nreturns a `ValidationVerdict`.\n\n**Body.** Send the raw XML as the request body with\n`Content-Type: application/xml` or `text/xml`, choosing the ruleset\nwith the query parameters. Or send `multipart/form-data` with a\n`document` part holding the XML and an optional `options` part\nholding a JSON `ValidationOptions` object; query parameters are not\nread for multipart requests. The multipart form is the one to use\nfrom browsers and low-code tools.\n\n**Strictness.** For raw XML bodies, a query parameter that is\nrepeated or not one of `ruleset`, `profile` and `document_type` is\n`400 malformed_request`, so a misspelt selector cannot silently fall\nback to a default. For multipart bodies, exactly one `document`\npart (an XML media type) and at most one `options` part\n(`application/json`) are accepted; a repeated or unknown part is\n`400 malformed_request`. A `Content-Encoding` other than identity is\n`415 unsupported_content_type`.\n\n**Correlation.** Every response carries `X-Ironfang-Request-ID`,\nIronfang's id for the request, which every Problem repeats as\n`request_id`. Send your own `X-Request-ID` and it is echoed back\nunchanged.\n\n**Limits.** The XML is at most 5 MiB (5,242,880 bytes), 64 levels\ndeep and 250,000 elements. Archives and compressed bodies\n(`Content-Encoding` other than identity, ZIP, gzip) are refused with\n`415 unsupported_content_type`; a larger body with\n`413 payload_too_large`. The validation deadline is 10 seconds; a\nvalidation that exceeds it is indeterminate (`504`).\n\n**Ruleset selection.** `ruleset` is either the keyword `latest` or an\nexact immutable ruleset id (`fwrs_...`).\n\n- `latest` resolves to the currently `sendable` release for the\n  document's profile family at the moment of the request. With a\n  `profile`, the result reports `selection_method: latest`; without\n  one, the family is detected from the document's `CustomizationID`\n  and `ProfileID` and the result reports `selection_method:\n  detected`. Detection is for validation only; it is never a\n  statement about what the document should be. A document whose\n  profile cannot be detected - including XML that does not parse -\n  is `422 profile_undetected`; name a `profile` or an exact ruleset\n  to obtain a verdict on it.\n- An exact id validates against that release whatever its state\n  among `scheduled` (migration testing), `sendable` (current) and\n  `historical` (audit and reproduction). The result reports\n  `selection_method: exact` and the release's `state`. An id that is\n  unknown - or names an internal candidate, which is\n  indistinguishable from unknown - is `422 unknown_ruleset`; a\n  withdrawn release is `410 ruleset_disabled`.\n\nThe result always names the resolved immutable id in `ruleset.id`.\n\n**Outcome versus status.** The result separates how far execution\ngot (`status`) from what the standard concluded (`outcome`). A\ndocument that fails the standard at any layer - including malformed\nXML at the `xml` layer - is a successfully processed validation:\nHTTP `200` with `outcome: invalid`, and every deeper layer that could\nnot run is reported as `skipped`, never omitted. Only the request\nitself being wrong, or Financewolf being unable to decide, produces a\nProblem.\n\n**Idempotency.** Authenticated requests may send an\n`Idempotency-Key` (8-128 characters of `A-Z a-z 0-9 _ -`). The key\nis scoped to the organisation and the validation endpoint. Replaying\nthe same request with the same key returns the original operation\nand its result - the same `operation_id`, no second execution, no\nsecond charge - even if `latest` has since come to mean a newer\nrelease. A request is identified by its content: reusing a key with\ndifferent document bytes (a different input SHA-256) or a different\nruleset, profile or document type is `409 idempotency_conflict`.\nRequests without a key always create a new operation. Anonymous\nrequests do not use the header.\n\n**Findings.** At most 1,000 findings are returned inline, in\ndeterministic order: layer order, then severity (`fatal` before\n`warning`), then `rule_id`, then `location`, then `message`.\n`findings_summary` always states how many were returned, how many\nexist, the cap, and whether truncation happened.\n\nSelect your UBL XML file in Body. The file is sent to Financewolf for validation.",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "disabled": true,
                "key": "Idempotency-Key",
                "value": "{{Idempotency-Key}}"
              },
              {
                "key": "Content-Type",
                "value": "application/xml"
              }
            ],
            "method": "POST",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "einvoices",
                "validate"
              ],
              "query": [
                {
                  "disabled": true,
                  "key": "ruleset",
                  "value": "{{ruleset}}"
                },
                {
                  "disabled": true,
                  "key": "profile",
                  "value": "{{profile}}"
                },
                {
                  "disabled": true,
                  "key": "document_type",
                  "value": "{{document_type}}"
                }
              ],
              "raw": "{{base_url}}/v1/einvoices/validate"
            }
          },
          "response": [
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"schema\": \"financewolf/einvoice/validation-result/v1\",\n  \"operation_id\": \"op_01J9Q4R6T8XZ2M3N5P7QSVWY9A\",\n  \"status\": \"completed\",\n  \"outcome\": \"valid\",\n  \"input\": {\n    \"sha256\": \"e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7\",\n    \"bytes\": 48211,\n    \"content_type\": \"application/xml\",\n    \"document_type\": \"invoice\"\n  },\n  \"ruleset\": {\n    \"requested\": \"fwrs_example_invoice_2025z\",\n    \"id\": \"fwrs_example_invoice_2025z\",\n    \"selection_method\": \"exact\",\n    \"state\": \"historical\",\n    \"vesid\": \"uk.ironfang.example:invoice:2025z\",\n    \"official_release\": \"Example release 2025z\"\n  },\n  \"profile\": {\n    \"family\": \"peppol-bis-billing-3\",\n    \"source\": \"requested\",\n    \"customization_id\": \"urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0\",\n    \"profile_id\": \"urn:fdc:peppol.eu:2017:poacc:billing:01:1.0\"\n  },\n  \"layers\": [\n    {\n      \"layer\": \"input\",\n      \"status\": \"passed\",\n      \"fatal\": 0,\n      \"warning\": 0\n    },\n    {\n      \"layer\": \"xml\",\n      \"status\": \"passed\",\n      \"fatal\": 0,\n      \"warning\": 0\n    },\n    {\n      \"layer\": \"xsd\",\n      \"status\": \"passed\",\n      \"fatal\": 0,\n      \"warning\": 0\n    },\n    {\n      \"layer\": \"en16931\",\n      \"status\": \"passed\",\n      \"fatal\": 0,\n      \"warning\": 0\n    },\n    {\n      \"layer\": \"peppol\",\n      \"status\": \"passed\",\n      \"fatal\": 0,\n      \"warning\": 0\n    }\n  ],\n  \"counts\": {\n    \"by_severity\": {\n      \"fatal\": 0,\n      \"warning\": 0\n    },\n    \"by_layer\": {\n      \"input\": 0,\n      \"xml\": 0,\n      \"xsd\": 0,\n      \"en16931\": 0,\n      \"peppol\": 0\n    }\n  },\n  \"findings\": [],\n  \"findings_summary\": {\n    \"returned\": 0,\n    \"total\": 0,\n    \"cap\": 1000,\n    \"truncated\": false\n  },\n  \"timing\": {\n    \"started_at\": \"2026-09-02T09:15:04Z\",\n    \"completed_at\": \"2026-09-02T09:15:05Z\",\n    \"duration_ms\": 842\n  },\n  \"engine\": {\n    \"name\": \"phive/12.1.0\",\n    \"image_digest\": \"sha256:4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f\"\n  },\n  \"links\": {\n    \"report\": \"https://api.ironfang.uk/financewolf/v1/einvoices/operations/op_01J9Q4R6T8XZ2M3N5P7QSVWY9A/report?sig=k1.7f3a\",\n    \"evidence\": \"https://api.ironfang.uk/financewolf/v1/einvoices/operations/op_01J9Q4R6T8XZ2M3N5P7QSVWY9A/evidence?sig=k1.9c2d\"\n  },\n  \"usage\": {\n    \"charged\": true,\n    \"credits\": 1,\n    \"remaining\": 499,\n    \"period_ends_at\": \"2026-10-01T00:00:00Z\"\n  }\n}",
              "code": 200,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "name": "200 Reproduction against an exact historical release",
              "originalRequest": {
                "body": {
                  "file": {
                    "src": ""
                  },
                  "mode": "file"
                },
                "description": "Validates a single UBL Invoice or CreditNote document synchronously and\nreturns a `ValidationVerdict`.\n\n**Body.** Send the raw XML as the request body with\n`Content-Type: application/xml` or `text/xml`, choosing the ruleset\nwith the query parameters. Or send `multipart/form-data` with a\n`document` part holding the XML and an optional `options` part\nholding a JSON `ValidationOptions` object; query parameters are not\nread for multipart requests. The multipart form is the one to use\nfrom browsers and low-code tools.\n\n**Strictness.** For raw XML bodies, a query parameter that is\nrepeated or not one of `ruleset`, `profile` and `document_type` is\n`400 malformed_request`, so a misspelt selector cannot silently fall\nback to a default. For multipart bodies, exactly one `document`\npart (an XML media type) and at most one `options` part\n(`application/json`) are accepted; a repeated or unknown part is\n`400 malformed_request`. A `Content-Encoding` other than identity is\n`415 unsupported_content_type`.\n\n**Correlation.** Every response carries `X-Ironfang-Request-ID`,\nIronfang's id for the request, which every Problem repeats as\n`request_id`. Send your own `X-Request-ID` and it is echoed back\nunchanged.\n\n**Limits.** The XML is at most 5 MiB (5,242,880 bytes), 64 levels\ndeep and 250,000 elements. Archives and compressed bodies\n(`Content-Encoding` other than identity, ZIP, gzip) are refused with\n`415 unsupported_content_type`; a larger body with\n`413 payload_too_large`. The validation deadline is 10 seconds; a\nvalidation that exceeds it is indeterminate (`504`).\n\n**Ruleset selection.** `ruleset` is either the keyword `latest` or an\nexact immutable ruleset id (`fwrs_...`).\n\n- `latest` resolves to the currently `sendable` release for the\n  document's profile family at the moment of the request. With a\n  `profile`, the result reports `selection_method: latest`; without\n  one, the family is detected from the document's `CustomizationID`\n  and `ProfileID` and the result reports `selection_method:\n  detected`. Detection is for validation only; it is never a\n  statement about what the document should be. A document whose\n  profile cannot be detected - including XML that does not parse -\n  is `422 profile_undetected`; name a `profile` or an exact ruleset\n  to obtain a verdict on it.\n- An exact id validates against that release whatever its state\n  among `scheduled` (migration testing), `sendable` (current) and\n  `historical` (audit and reproduction). The result reports\n  `selection_method: exact` and the release's `state`. An id that is\n  unknown - or names an internal candidate, which is\n  indistinguishable from unknown - is `422 unknown_ruleset`; a\n  withdrawn release is `410 ruleset_disabled`.\n\nThe result always names the resolved immutable id in `ruleset.id`.\n\n**Outcome versus status.** The result separates how far execution\ngot (`status`) from what the standard concluded (`outcome`). A\ndocument that fails the standard at any layer - including malformed\nXML at the `xml` layer - is a successfully processed validation:\nHTTP `200` with `outcome: invalid`, and every deeper layer that could\nnot run is reported as `skipped`, never omitted. Only the request\nitself being wrong, or Financewolf being unable to decide, produces a\nProblem.\n\n**Idempotency.** Authenticated requests may send an\n`Idempotency-Key` (8-128 characters of `A-Z a-z 0-9 _ -`). The key\nis scoped to the organisation and the validation endpoint. Replaying\nthe same request with the same key returns the original operation\nand its result - the same `operation_id`, no second execution, no\nsecond charge - even if `latest` has since come to mean a newer\nrelease. A request is identified by its content: reusing a key with\ndifferent document bytes (a different input SHA-256) or a different\nruleset, profile or document type is `409 idempotency_conflict`.\nRequests without a key always create a new operation. Anonymous\nrequests do not use the header.\n\n**Findings.** At most 1,000 findings are returned inline, in\ndeterministic order: layer order, then severity (`fatal` before\n`warning`), then `rule_id`, then `location`, then `message`.\n`findings_summary` always states how many were returned, how many\nexist, the cap, and whether truncation happened.\n\nSelect your UBL XML file in Body. The file is sent to Financewolf for validation.",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "disabled": true,
                    "key": "Idempotency-Key",
                    "value": "{{Idempotency-Key}}"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/xml"
                  }
                ],
                "method": "POST",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "validate"
                  ],
                  "query": [
                    {
                      "disabled": true,
                      "key": "ruleset",
                      "value": "{{ruleset}}"
                    },
                    {
                      "disabled": true,
                      "key": "profile",
                      "value": "{{profile}}"
                    },
                    {
                      "disabled": true,
                      "key": "document_type",
                      "value": "{{document_type}}"
                    }
                  ],
                  "raw": "{{base_url}}/v1/einvoices/validate"
                }
              },
              "status": "OK"
            },
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"schema\": \"financewolf/einvoice/validation-result/v1\",\n  \"status\": \"completed\",\n  \"outcome\": \"invalid\",\n  \"input\": {\n    \"sha256\": \"d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2\",\n    \"bytes\": 48211,\n    \"content_type\": \"application/xml\",\n    \"document_type\": \"invoice\"\n  },\n  \"ruleset\": {\n    \"requested\": \"fwrs_example_invoice_2026b\",\n    \"id\": \"fwrs_example_invoice_2026b\",\n    \"selection_method\": \"exact\",\n    \"state\": \"scheduled\",\n    \"vesid\": \"uk.ironfang.example:invoice:2026b\",\n    \"official_release\": \"Example release 2026b\"\n  },\n  \"profile\": {\n    \"family\": \"peppol-bis-billing-3\",\n    \"source\": \"requested\",\n    \"customization_id\": \"urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0\",\n    \"profile_id\": \"urn:fdc:peppol.eu:2017:poacc:billing:01:1.0\"\n  },\n  \"layers\": [\n    {\n      \"layer\": \"input\",\n      \"status\": \"passed\",\n      \"fatal\": 0,\n      \"warning\": 0\n    },\n    {\n      \"layer\": \"xml\",\n      \"status\": \"passed\",\n      \"fatal\": 0,\n      \"warning\": 0\n    },\n    {\n      \"layer\": \"xsd\",\n      \"status\": \"passed\",\n      \"fatal\": 0,\n      \"warning\": 0\n    },\n    {\n      \"layer\": \"en16931\",\n      \"status\": \"passed\",\n      \"fatal\": 0,\n      \"warning\": 0\n    },\n    {\n      \"layer\": \"peppol\",\n      \"status\": \"failed\",\n      \"fatal\": 1,\n      \"warning\": 0\n    }\n  ],\n  \"counts\": {\n    \"by_severity\": {\n      \"fatal\": 1,\n      \"warning\": 0\n    },\n    \"by_layer\": {\n      \"input\": 0,\n      \"xml\": 0,\n      \"xsd\": 0,\n      \"en16931\": 0,\n      \"peppol\": 1\n    }\n  },\n  \"findings\": [\n    {\n      \"severity\": \"fatal\",\n      \"layer\": \"peppol\",\n      \"rule_id\": \"PEPPOL-EN16931-R061\",\n      \"message\": \"A Peppol BIS Billing 3.0 rule failed at this location.\",\n      \"location\": \"/Invoice/cac:PaymentMeans\",\n      \"location_kind\": \"xpath\",\n      \"source_url\": \"https://example.test/rules/PEPPOL-EN16931-R061\",\n      \"ruleset_id\": \"fwrs_example_invoice_2026b\"\n    }\n  ],\n  \"findings_summary\": {\n    \"returned\": 1,\n    \"total\": 1,\n    \"cap\": 1000,\n    \"truncated\": false\n  },\n  \"timing\": {\n    \"started_at\": \"2026-09-02T09:15:04Z\",\n    \"completed_at\": \"2026-09-02T09:15:05Z\",\n    \"duration_ms\": 842\n  },\n  \"engine\": {\n    \"name\": \"phive/12.1.0\",\n    \"image_digest\": \"sha256:4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f\"\n  }\n}",
              "code": 200,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "name": "200 Migration testing against an exact scheduled release",
              "originalRequest": {
                "body": {
                  "file": {
                    "src": ""
                  },
                  "mode": "file"
                },
                "description": "Validates a single UBL Invoice or CreditNote document synchronously and\nreturns a `ValidationVerdict`.\n\n**Body.** Send the raw XML as the request body with\n`Content-Type: application/xml` or `text/xml`, choosing the ruleset\nwith the query parameters. Or send `multipart/form-data` with a\n`document` part holding the XML and an optional `options` part\nholding a JSON `ValidationOptions` object; query parameters are not\nread for multipart requests. The multipart form is the one to use\nfrom browsers and low-code tools.\n\n**Strictness.** For raw XML bodies, a query parameter that is\nrepeated or not one of `ruleset`, `profile` and `document_type` is\n`400 malformed_request`, so a misspelt selector cannot silently fall\nback to a default. For multipart bodies, exactly one `document`\npart (an XML media type) and at most one `options` part\n(`application/json`) are accepted; a repeated or unknown part is\n`400 malformed_request`. A `Content-Encoding` other than identity is\n`415 unsupported_content_type`.\n\n**Correlation.** Every response carries `X-Ironfang-Request-ID`,\nIronfang's id for the request, which every Problem repeats as\n`request_id`. Send your own `X-Request-ID` and it is echoed back\nunchanged.\n\n**Limits.** The XML is at most 5 MiB (5,242,880 bytes), 64 levels\ndeep and 250,000 elements. Archives and compressed bodies\n(`Content-Encoding` other than identity, ZIP, gzip) are refused with\n`415 unsupported_content_type`; a larger body with\n`413 payload_too_large`. The validation deadline is 10 seconds; a\nvalidation that exceeds it is indeterminate (`504`).\n\n**Ruleset selection.** `ruleset` is either the keyword `latest` or an\nexact immutable ruleset id (`fwrs_...`).\n\n- `latest` resolves to the currently `sendable` release for the\n  document's profile family at the moment of the request. With a\n  `profile`, the result reports `selection_method: latest`; without\n  one, the family is detected from the document's `CustomizationID`\n  and `ProfileID` and the result reports `selection_method:\n  detected`. Detection is for validation only; it is never a\n  statement about what the document should be. A document whose\n  profile cannot be detected - including XML that does not parse -\n  is `422 profile_undetected`; name a `profile` or an exact ruleset\n  to obtain a verdict on it.\n- An exact id validates against that release whatever its state\n  among `scheduled` (migration testing), `sendable` (current) and\n  `historical` (audit and reproduction). The result reports\n  `selection_method: exact` and the release's `state`. An id that is\n  unknown - or names an internal candidate, which is\n  indistinguishable from unknown - is `422 unknown_ruleset`; a\n  withdrawn release is `410 ruleset_disabled`.\n\nThe result always names the resolved immutable id in `ruleset.id`.\n\n**Outcome versus status.** The result separates how far execution\ngot (`status`) from what the standard concluded (`outcome`). A\ndocument that fails the standard at any layer - including malformed\nXML at the `xml` layer - is a successfully processed validation:\nHTTP `200` with `outcome: invalid`, and every deeper layer that could\nnot run is reported as `skipped`, never omitted. Only the request\nitself being wrong, or Financewolf being unable to decide, produces a\nProblem.\n\n**Idempotency.** Authenticated requests may send an\n`Idempotency-Key` (8-128 characters of `A-Z a-z 0-9 _ -`). The key\nis scoped to the organisation and the validation endpoint. Replaying\nthe same request with the same key returns the original operation\nand its result - the same `operation_id`, no second execution, no\nsecond charge - even if `latest` has since come to mean a newer\nrelease. A request is identified by its content: reusing a key with\ndifferent document bytes (a different input SHA-256) or a different\nruleset, profile or document type is `409 idempotency_conflict`.\nRequests without a key always create a new operation. Anonymous\nrequests do not use the header.\n\n**Findings.** At most 1,000 findings are returned inline, in\ndeterministic order: layer order, then severity (`fatal` before\n`warning`), then `rule_id`, then `location`, then `message`.\n`findings_summary` always states how many were returned, how many\nexist, the cap, and whether truncation happened.\n\nSelect your UBL XML file in Body. The file is sent to Financewolf for validation.",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "disabled": true,
                    "key": "Idempotency-Key",
                    "value": "{{Idempotency-Key}}"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/xml"
                  }
                ],
                "method": "POST",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "validate"
                  ],
                  "query": [
                    {
                      "disabled": true,
                      "key": "ruleset",
                      "value": "{{ruleset}}"
                    },
                    {
                      "disabled": true,
                      "key": "profile",
                      "value": "{{profile}}"
                    },
                    {
                      "disabled": true,
                      "key": "document_type",
                      "value": "{{document_type}}"
                    }
                  ],
                  "raw": "{{base_url}}/v1/einvoices/validate"
                }
              },
              "status": "OK"
            },
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"schema\": \"financewolf/einvoice/validation-result/v1\",\n  \"status\": \"completed\",\n  \"outcome\": \"invalid\",\n  \"input\": {\n    \"sha256\": \"3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c\",\n    \"bytes\": 48211,\n    \"content_type\": \"application/xml\",\n    \"document_type\": \"invoice\"\n  },\n  \"ruleset\": {\n    \"requested\": \"latest\",\n    \"id\": \"fwrs_example_invoice_2026a\",\n    \"selection_method\": \"detected\",\n    \"state\": \"sendable\",\n    \"vesid\": \"uk.ironfang.example:invoice:2026a\",\n    \"official_release\": \"Example release 2026a\"\n  },\n  \"profile\": {\n    \"family\": \"peppol-bis-billing-3\",\n    \"source\": \"detected\",\n    \"customization_id\": \"urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0\",\n    \"profile_id\": \"urn:fdc:peppol.eu:2017:poacc:billing:01:1.0\"\n  },\n  \"layers\": [\n    {\n      \"layer\": \"input\",\n      \"status\": \"passed\",\n      \"fatal\": 0,\n      \"warning\": 0\n    },\n    {\n      \"layer\": \"xml\",\n      \"status\": \"passed\",\n      \"fatal\": 0,\n      \"warning\": 0\n    },\n    {\n      \"layer\": \"xsd\",\n      \"status\": \"passed\",\n      \"fatal\": 0,\n      \"warning\": 0\n    },\n    {\n      \"layer\": \"en16931\",\n      \"status\": \"failed\",\n      \"fatal\": 1,\n      \"warning\": 0\n    },\n    {\n      \"layer\": \"peppol\",\n      \"status\": \"failed\",\n      \"fatal\": 1,\n      \"warning\": 1\n    }\n  ],\n  \"counts\": {\n    \"by_severity\": {\n      \"fatal\": 2,\n      \"warning\": 1\n    },\n    \"by_layer\": {\n      \"input\": 0,\n      \"xml\": 0,\n      \"xsd\": 0,\n      \"en16931\": 1,\n      \"peppol\": 2\n    }\n  },\n  \"findings\": [\n    {\n      \"severity\": \"fatal\",\n      \"layer\": \"en16931\",\n      \"rule_id\": \"BR-CO-10\",\n      \"message\": \"An EN 16931 business rule failed at this location.\",\n      \"location\": \"/Invoice/cac:LegalMonetaryTotal/cbc:LineExtensionAmount\",\n      \"location_kind\": \"xpath\",\n      \"source_url\": \"https://example.test/rules/BR-CO-10\",\n      \"ruleset_id\": \"fwrs_example_invoice_2026a\"\n    },\n    {\n      \"severity\": \"fatal\",\n      \"layer\": \"peppol\",\n      \"rule_id\": \"PEPPOL-EN16931-R020\",\n      \"message\": \"A Peppol BIS Billing 3.0 rule failed at this location.\",\n      \"location\": \"/Invoice/cac:AccountingSupplierParty/cac:Party\",\n      \"location_kind\": \"xpath\",\n      \"hint_code\": \"seller-electronic-address-required\",\n      \"hint\": \"Add cbc:EndpointID with a schemeID under AccountingSupplierParty/Party.\",\n      \"source_url\": \"https://example.test/rules/PEPPOL-EN16931-R020\",\n      \"ruleset_id\": \"fwrs_example_invoice_2026a\"\n    },\n    {\n      \"severity\": \"warning\",\n      \"layer\": \"peppol\",\n      \"rule_id\": \"PEPPOL-EN16931-R110\",\n      \"message\": \"A Peppol BIS Billing 3.0 rule failed at this location.\",\n      \"location\": \"/Invoice/cac:InvoiceLine[3]/cac:InvoicePeriod/cbc:StartDate\",\n      \"location_kind\": \"xpath\",\n      \"source_url\": \"https://example.test/rules/PEPPOL-EN16931-R110\",\n      \"ruleset_id\": \"fwrs_example_invoice_2026a\"\n    }\n  ],\n  \"findings_summary\": {\n    \"returned\": 3,\n    \"total\": 3,\n    \"cap\": 1000,\n    \"truncated\": false\n  },\n  \"timing\": {\n    \"started_at\": \"2026-09-02T09:15:04Z\",\n    \"completed_at\": \"2026-09-02T09:15:05Z\",\n    \"duration_ms\": 842\n  },\n  \"engine\": {\n    \"name\": \"phive/12.1.0\",\n    \"image_digest\": \"sha256:4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f\",\n    \"hint_pack_version\": \"example.1\"\n  }\n}",
              "code": 200,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "name": "200 An invoice that fails Peppol rules, anonymous, profile detected",
              "originalRequest": {
                "body": {
                  "file": {
                    "src": ""
                  },
                  "mode": "file"
                },
                "description": "Validates a single UBL Invoice or CreditNote document synchronously and\nreturns a `ValidationVerdict`.\n\n**Body.** Send the raw XML as the request body with\n`Content-Type: application/xml` or `text/xml`, choosing the ruleset\nwith the query parameters. Or send `multipart/form-data` with a\n`document` part holding the XML and an optional `options` part\nholding a JSON `ValidationOptions` object; query parameters are not\nread for multipart requests. The multipart form is the one to use\nfrom browsers and low-code tools.\n\n**Strictness.** For raw XML bodies, a query parameter that is\nrepeated or not one of `ruleset`, `profile` and `document_type` is\n`400 malformed_request`, so a misspelt selector cannot silently fall\nback to a default. For multipart bodies, exactly one `document`\npart (an XML media type) and at most one `options` part\n(`application/json`) are accepted; a repeated or unknown part is\n`400 malformed_request`. A `Content-Encoding` other than identity is\n`415 unsupported_content_type`.\n\n**Correlation.** Every response carries `X-Ironfang-Request-ID`,\nIronfang's id for the request, which every Problem repeats as\n`request_id`. Send your own `X-Request-ID` and it is echoed back\nunchanged.\n\n**Limits.** The XML is at most 5 MiB (5,242,880 bytes), 64 levels\ndeep and 250,000 elements. Archives and compressed bodies\n(`Content-Encoding` other than identity, ZIP, gzip) are refused with\n`415 unsupported_content_type`; a larger body with\n`413 payload_too_large`. The validation deadline is 10 seconds; a\nvalidation that exceeds it is indeterminate (`504`).\n\n**Ruleset selection.** `ruleset` is either the keyword `latest` or an\nexact immutable ruleset id (`fwrs_...`).\n\n- `latest` resolves to the currently `sendable` release for the\n  document's profile family at the moment of the request. With a\n  `profile`, the result reports `selection_method: latest`; without\n  one, the family is detected from the document's `CustomizationID`\n  and `ProfileID` and the result reports `selection_method:\n  detected`. Detection is for validation only; it is never a\n  statement about what the document should be. A document whose\n  profile cannot be detected - including XML that does not parse -\n  is `422 profile_undetected`; name a `profile` or an exact ruleset\n  to obtain a verdict on it.\n- An exact id validates against that release whatever its state\n  among `scheduled` (migration testing), `sendable` (current) and\n  `historical` (audit and reproduction). The result reports\n  `selection_method: exact` and the release's `state`. An id that is\n  unknown - or names an internal candidate, which is\n  indistinguishable from unknown - is `422 unknown_ruleset`; a\n  withdrawn release is `410 ruleset_disabled`.\n\nThe result always names the resolved immutable id in `ruleset.id`.\n\n**Outcome versus status.** The result separates how far execution\ngot (`status`) from what the standard concluded (`outcome`). A\ndocument that fails the standard at any layer - including malformed\nXML at the `xml` layer - is a successfully processed validation:\nHTTP `200` with `outcome: invalid`, and every deeper layer that could\nnot run is reported as `skipped`, never omitted. Only the request\nitself being wrong, or Financewolf being unable to decide, produces a\nProblem.\n\n**Idempotency.** Authenticated requests may send an\n`Idempotency-Key` (8-128 characters of `A-Z a-z 0-9 _ -`). The key\nis scoped to the organisation and the validation endpoint. Replaying\nthe same request with the same key returns the original operation\nand its result - the same `operation_id`, no second execution, no\nsecond charge - even if `latest` has since come to mean a newer\nrelease. A request is identified by its content: reusing a key with\ndifferent document bytes (a different input SHA-256) or a different\nruleset, profile or document type is `409 idempotency_conflict`.\nRequests without a key always create a new operation. Anonymous\nrequests do not use the header.\n\n**Findings.** At most 1,000 findings are returned inline, in\ndeterministic order: layer order, then severity (`fatal` before\n`warning`), then `rule_id`, then `location`, then `message`.\n`findings_summary` always states how many were returned, how many\nexist, the cap, and whether truncation happened.\n\nSelect your UBL XML file in Body. The file is sent to Financewolf for validation.",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "disabled": true,
                    "key": "Idempotency-Key",
                    "value": "{{Idempotency-Key}}"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/xml"
                  }
                ],
                "method": "POST",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "validate"
                  ],
                  "query": [
                    {
                      "disabled": true,
                      "key": "ruleset",
                      "value": "{{ruleset}}"
                    },
                    {
                      "disabled": true,
                      "key": "profile",
                      "value": "{{profile}}"
                    },
                    {
                      "disabled": true,
                      "key": "document_type",
                      "value": "{{document_type}}"
                    }
                  ],
                  "raw": "{{base_url}}/v1/einvoices/validate"
                }
              },
              "status": "OK"
            },
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"schema\": \"financewolf/einvoice/validation-result/v1\",\n  \"status\": \"completed\",\n  \"outcome\": \"invalid\",\n  \"input\": {\n    \"sha256\": \"a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1\",\n    \"bytes\": 3187,\n    \"content_type\": \"text/xml\",\n    \"document_type\": \"unknown\"\n  },\n  \"ruleset\": {\n    \"requested\": \"fwrs_example_invoice_2026a\",\n    \"id\": \"fwrs_example_invoice_2026a\",\n    \"selection_method\": \"exact\",\n    \"state\": \"sendable\",\n    \"vesid\": \"uk.ironfang.example:invoice:2026a\",\n    \"official_release\": \"Example release 2026a\"\n  },\n  \"profile\": {\n    \"family\": \"peppol-bis-billing-3\",\n    \"source\": \"requested\"\n  },\n  \"layers\": [\n    {\n      \"layer\": \"input\",\n      \"status\": \"passed\",\n      \"fatal\": 0,\n      \"warning\": 0\n    },\n    {\n      \"layer\": \"xml\",\n      \"status\": \"failed\",\n      \"fatal\": 1,\n      \"warning\": 0\n    },\n    {\n      \"layer\": \"xsd\",\n      \"status\": \"skipped\",\n      \"reason\": \"skipped_after_failure\",\n      \"fatal\": 0,\n      \"warning\": 0\n    },\n    {\n      \"layer\": \"en16931\",\n      \"status\": \"skipped\",\n      \"reason\": \"skipped_after_failure\",\n      \"fatal\": 0,\n      \"warning\": 0\n    },\n    {\n      \"layer\": \"peppol\",\n      \"status\": \"skipped\",\n      \"reason\": \"skipped_after_failure\",\n      \"fatal\": 0,\n      \"warning\": 0\n    }\n  ],\n  \"counts\": {\n    \"by_severity\": {\n      \"fatal\": 1,\n      \"warning\": 0\n    },\n    \"by_layer\": {\n      \"input\": 0,\n      \"xml\": 1,\n      \"xsd\": 0,\n      \"en16931\": 0,\n      \"peppol\": 0\n    }\n  },\n  \"findings\": [\n    {\n      \"severity\": \"fatal\",\n      \"layer\": \"xml\",\n      \"rule_id\": \"xml.not-well-formed\",\n      \"message\": \"The document could not be read as XML within the contract limits.\",\n      \"location\": \"line 212, column 9\",\n      \"location_kind\": \"line-column\",\n      \"ruleset_id\": \"fwrs_example_invoice_2026a\"\n    }\n  ],\n  \"findings_summary\": {\n    \"returned\": 1,\n    \"total\": 1,\n    \"cap\": 1000,\n    \"truncated\": false\n  },\n  \"timing\": {\n    \"started_at\": \"2026-09-02T09:15:04Z\",\n    \"completed_at\": \"2026-09-02T09:15:05Z\",\n    \"duration_ms\": 842\n  },\n  \"engine\": {\n    \"name\": \"phive/12.1.0\",\n    \"image_digest\": \"sha256:4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f\"\n  }\n}",
              "code": 200,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "name": "200 Malformed XML against an exact ruleset - deeper layers skipped",
              "originalRequest": {
                "body": {
                  "file": {
                    "src": ""
                  },
                  "mode": "file"
                },
                "description": "Validates a single UBL Invoice or CreditNote document synchronously and\nreturns a `ValidationVerdict`.\n\n**Body.** Send the raw XML as the request body with\n`Content-Type: application/xml` or `text/xml`, choosing the ruleset\nwith the query parameters. Or send `multipart/form-data` with a\n`document` part holding the XML and an optional `options` part\nholding a JSON `ValidationOptions` object; query parameters are not\nread for multipart requests. The multipart form is the one to use\nfrom browsers and low-code tools.\n\n**Strictness.** For raw XML bodies, a query parameter that is\nrepeated or not one of `ruleset`, `profile` and `document_type` is\n`400 malformed_request`, so a misspelt selector cannot silently fall\nback to a default. For multipart bodies, exactly one `document`\npart (an XML media type) and at most one `options` part\n(`application/json`) are accepted; a repeated or unknown part is\n`400 malformed_request`. A `Content-Encoding` other than identity is\n`415 unsupported_content_type`.\n\n**Correlation.** Every response carries `X-Ironfang-Request-ID`,\nIronfang's id for the request, which every Problem repeats as\n`request_id`. Send your own `X-Request-ID` and it is echoed back\nunchanged.\n\n**Limits.** The XML is at most 5 MiB (5,242,880 bytes), 64 levels\ndeep and 250,000 elements. Archives and compressed bodies\n(`Content-Encoding` other than identity, ZIP, gzip) are refused with\n`415 unsupported_content_type`; a larger body with\n`413 payload_too_large`. The validation deadline is 10 seconds; a\nvalidation that exceeds it is indeterminate (`504`).\n\n**Ruleset selection.** `ruleset` is either the keyword `latest` or an\nexact immutable ruleset id (`fwrs_...`).\n\n- `latest` resolves to the currently `sendable` release for the\n  document's profile family at the moment of the request. With a\n  `profile`, the result reports `selection_method: latest`; without\n  one, the family is detected from the document's `CustomizationID`\n  and `ProfileID` and the result reports `selection_method:\n  detected`. Detection is for validation only; it is never a\n  statement about what the document should be. A document whose\n  profile cannot be detected - including XML that does not parse -\n  is `422 profile_undetected`; name a `profile` or an exact ruleset\n  to obtain a verdict on it.\n- An exact id validates against that release whatever its state\n  among `scheduled` (migration testing), `sendable` (current) and\n  `historical` (audit and reproduction). The result reports\n  `selection_method: exact` and the release's `state`. An id that is\n  unknown - or names an internal candidate, which is\n  indistinguishable from unknown - is `422 unknown_ruleset`; a\n  withdrawn release is `410 ruleset_disabled`.\n\nThe result always names the resolved immutable id in `ruleset.id`.\n\n**Outcome versus status.** The result separates how far execution\ngot (`status`) from what the standard concluded (`outcome`). A\ndocument that fails the standard at any layer - including malformed\nXML at the `xml` layer - is a successfully processed validation:\nHTTP `200` with `outcome: invalid`, and every deeper layer that could\nnot run is reported as `skipped`, never omitted. Only the request\nitself being wrong, or Financewolf being unable to decide, produces a\nProblem.\n\n**Idempotency.** Authenticated requests may send an\n`Idempotency-Key` (8-128 characters of `A-Z a-z 0-9 _ -`). The key\nis scoped to the organisation and the validation endpoint. Replaying\nthe same request with the same key returns the original operation\nand its result - the same `operation_id`, no second execution, no\nsecond charge - even if `latest` has since come to mean a newer\nrelease. A request is identified by its content: reusing a key with\ndifferent document bytes (a different input SHA-256) or a different\nruleset, profile or document type is `409 idempotency_conflict`.\nRequests without a key always create a new operation. Anonymous\nrequests do not use the header.\n\n**Findings.** At most 1,000 findings are returned inline, in\ndeterministic order: layer order, then severity (`fatal` before\n`warning`), then `rule_id`, then `location`, then `message`.\n`findings_summary` always states how many were returned, how many\nexist, the cap, and whether truncation happened.\n\nSelect your UBL XML file in Body. The file is sent to Financewolf for validation.",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "disabled": true,
                    "key": "Idempotency-Key",
                    "value": "{{Idempotency-Key}}"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/xml"
                  }
                ],
                "method": "POST",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "validate"
                  ],
                  "query": [
                    {
                      "disabled": true,
                      "key": "ruleset",
                      "value": "{{ruleset}}"
                    },
                    {
                      "disabled": true,
                      "key": "profile",
                      "value": "{{profile}}"
                    },
                    {
                      "disabled": true,
                      "key": "document_type",
                      "value": "{{document_type}}"
                    }
                  ],
                  "raw": "{{base_url}}/v1/einvoices/validate"
                }
              },
              "status": "OK"
            },
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"schema\": \"financewolf/einvoice/validation-result/v1\",\n  \"operation_id\": \"op_01J9Q4R6T8XZ2M3N5P7QSVWY9A\",\n  \"status\": \"completed\",\n  \"outcome\": \"valid\",\n  \"input\": {\n    \"sha256\": \"9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b\",\n    \"bytes\": 48211,\n    \"content_type\": \"application/xml\",\n    \"document_type\": \"invoice\"\n  },\n  \"ruleset\": {\n    \"requested\": \"latest\",\n    \"id\": \"fwrs_example_invoice_2026a\",\n    \"selection_method\": \"latest\",\n    \"state\": \"sendable\",\n    \"vesid\": \"uk.ironfang.example:invoice:2026a\",\n    \"official_release\": \"Example release 2026a\"\n  },\n  \"profile\": {\n    \"family\": \"peppol-bis-billing-3\",\n    \"source\": \"requested\",\n    \"customization_id\": \"urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0\",\n    \"profile_id\": \"urn:fdc:peppol.eu:2017:poacc:billing:01:1.0\"\n  },\n  \"layers\": [\n    {\n      \"layer\": \"input\",\n      \"status\": \"passed\",\n      \"fatal\": 0,\n      \"warning\": 0\n    },\n    {\n      \"layer\": \"xml\",\n      \"status\": \"passed\",\n      \"fatal\": 0,\n      \"warning\": 0\n    },\n    {\n      \"layer\": \"xsd\",\n      \"status\": \"passed\",\n      \"fatal\": 0,\n      \"warning\": 0\n    },\n    {\n      \"layer\": \"en16931\",\n      \"status\": \"passed\",\n      \"fatal\": 0,\n      \"warning\": 0\n    },\n    {\n      \"layer\": \"peppol\",\n      \"status\": \"passed\",\n      \"fatal\": 0,\n      \"warning\": 0\n    }\n  ],\n  \"counts\": {\n    \"by_severity\": {\n      \"fatal\": 0,\n      \"warning\": 0\n    },\n    \"by_layer\": {\n      \"input\": 0,\n      \"xml\": 0,\n      \"xsd\": 0,\n      \"en16931\": 0,\n      \"peppol\": 0\n    }\n  },\n  \"findings\": [],\n  \"findings_summary\": {\n    \"returned\": 0,\n    \"total\": 0,\n    \"cap\": 1000,\n    \"truncated\": false\n  },\n  \"timing\": {\n    \"started_at\": \"2026-09-02T09:15:04Z\",\n    \"completed_at\": \"2026-09-02T09:15:05Z\",\n    \"duration_ms\": 842\n  },\n  \"engine\": {\n    \"name\": \"phive/12.1.0\",\n    \"image_digest\": \"sha256:4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f\"\n  },\n  \"links\": {\n    \"report\": \"https://api.ironfang.uk/financewolf/v1/einvoices/operations/op_01J9Q4R6T8XZ2M3N5P7QSVWY9A/report?sig=k1.7f3a\",\n    \"evidence\": \"https://api.ironfang.uk/financewolf/v1/einvoices/operations/op_01J9Q4R6T8XZ2M3N5P7QSVWY9A/evidence?sig=k1.9c2d\"\n  },\n  \"usage\": {\n    \"charged\": true,\n    \"credits\": 1,\n    \"remaining\": 499,\n    \"period_ends_at\": \"2026-10-01T00:00:00Z\"\n  }\n}",
              "code": 200,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "name": "200 A conformant invoice, authenticated, latest within a named profile",
              "originalRequest": {
                "body": {
                  "file": {
                    "src": ""
                  },
                  "mode": "file"
                },
                "description": "Validates a single UBL Invoice or CreditNote document synchronously and\nreturns a `ValidationVerdict`.\n\n**Body.** Send the raw XML as the request body with\n`Content-Type: application/xml` or `text/xml`, choosing the ruleset\nwith the query parameters. Or send `multipart/form-data` with a\n`document` part holding the XML and an optional `options` part\nholding a JSON `ValidationOptions` object; query parameters are not\nread for multipart requests. The multipart form is the one to use\nfrom browsers and low-code tools.\n\n**Strictness.** For raw XML bodies, a query parameter that is\nrepeated or not one of `ruleset`, `profile` and `document_type` is\n`400 malformed_request`, so a misspelt selector cannot silently fall\nback to a default. For multipart bodies, exactly one `document`\npart (an XML media type) and at most one `options` part\n(`application/json`) are accepted; a repeated or unknown part is\n`400 malformed_request`. A `Content-Encoding` other than identity is\n`415 unsupported_content_type`.\n\n**Correlation.** Every response carries `X-Ironfang-Request-ID`,\nIronfang's id for the request, which every Problem repeats as\n`request_id`. Send your own `X-Request-ID` and it is echoed back\nunchanged.\n\n**Limits.** The XML is at most 5 MiB (5,242,880 bytes), 64 levels\ndeep and 250,000 elements. Archives and compressed bodies\n(`Content-Encoding` other than identity, ZIP, gzip) are refused with\n`415 unsupported_content_type`; a larger body with\n`413 payload_too_large`. The validation deadline is 10 seconds; a\nvalidation that exceeds it is indeterminate (`504`).\n\n**Ruleset selection.** `ruleset` is either the keyword `latest` or an\nexact immutable ruleset id (`fwrs_...`).\n\n- `latest` resolves to the currently `sendable` release for the\n  document's profile family at the moment of the request. With a\n  `profile`, the result reports `selection_method: latest`; without\n  one, the family is detected from the document's `CustomizationID`\n  and `ProfileID` and the result reports `selection_method:\n  detected`. Detection is for validation only; it is never a\n  statement about what the document should be. A document whose\n  profile cannot be detected - including XML that does not parse -\n  is `422 profile_undetected`; name a `profile` or an exact ruleset\n  to obtain a verdict on it.\n- An exact id validates against that release whatever its state\n  among `scheduled` (migration testing), `sendable` (current) and\n  `historical` (audit and reproduction). The result reports\n  `selection_method: exact` and the release's `state`. An id that is\n  unknown - or names an internal candidate, which is\n  indistinguishable from unknown - is `422 unknown_ruleset`; a\n  withdrawn release is `410 ruleset_disabled`.\n\nThe result always names the resolved immutable id in `ruleset.id`.\n\n**Outcome versus status.** The result separates how far execution\ngot (`status`) from what the standard concluded (`outcome`). A\ndocument that fails the standard at any layer - including malformed\nXML at the `xml` layer - is a successfully processed validation:\nHTTP `200` with `outcome: invalid`, and every deeper layer that could\nnot run is reported as `skipped`, never omitted. Only the request\nitself being wrong, or Financewolf being unable to decide, produces a\nProblem.\n\n**Idempotency.** Authenticated requests may send an\n`Idempotency-Key` (8-128 characters of `A-Z a-z 0-9 _ -`). The key\nis scoped to the organisation and the validation endpoint. Replaying\nthe same request with the same key returns the original operation\nand its result - the same `operation_id`, no second execution, no\nsecond charge - even if `latest` has since come to mean a newer\nrelease. A request is identified by its content: reusing a key with\ndifferent document bytes (a different input SHA-256) or a different\nruleset, profile or document type is `409 idempotency_conflict`.\nRequests without a key always create a new operation. Anonymous\nrequests do not use the header.\n\n**Findings.** At most 1,000 findings are returned inline, in\ndeterministic order: layer order, then severity (`fatal` before\n`warning`), then `rule_id`, then `location`, then `message`.\n`findings_summary` always states how many were returned, how many\nexist, the cap, and whether truncation happened.\n\nSelect your UBL XML file in Body. The file is sent to Financewolf for validation.",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "disabled": true,
                    "key": "Idempotency-Key",
                    "value": "{{Idempotency-Key}}"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/xml"
                  }
                ],
                "method": "POST",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "validate"
                  ],
                  "query": [
                    {
                      "disabled": true,
                      "key": "ruleset",
                      "value": "{{ruleset}}"
                    },
                    {
                      "disabled": true,
                      "key": "profile",
                      "value": "{{profile}}"
                    },
                    {
                      "disabled": true,
                      "key": "document_type",
                      "value": "{{document_type}}"
                    }
                  ],
                  "raw": "{{base_url}}/v1/einvoices/validate"
                }
              },
              "status": "OK"
            },
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"type\": \"https://ironfang.uk/problems/malformed_request\",\n  \"title\": \"Malformed request\",\n  \"status\": 400,\n  \"detail\": \"One or more options are invalid.\",\n  \"instance\": \"/financewolf/v1/einvoices/validate\",\n  \"code\": \"malformed_request\",\n  \"request_id\": \"req_01J9Q4TPD8XZM9P6Q2R8TVYB5G\",\n  \"errors\": [\n    {\n      \"field\": \"options.document_type\",\n      \"code\": \"invalid_value\",\n      \"message\": \"document_type must be invoice, credit_note or auto.\"\n    },\n    {\n      \"field\": \"options.deadline\",\n      \"code\": \"unknown_field\",\n      \"message\": \"deadline is not an option.\"\n    }\n  ]\n}",
              "code": 400,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/problem+json"
                }
              ],
              "name": "400 Bad option values",
              "originalRequest": {
                "body": {
                  "file": {
                    "src": ""
                  },
                  "mode": "file"
                },
                "description": "Validates a single UBL Invoice or CreditNote document synchronously and\nreturns a `ValidationVerdict`.\n\n**Body.** Send the raw XML as the request body with\n`Content-Type: application/xml` or `text/xml`, choosing the ruleset\nwith the query parameters. Or send `multipart/form-data` with a\n`document` part holding the XML and an optional `options` part\nholding a JSON `ValidationOptions` object; query parameters are not\nread for multipart requests. The multipart form is the one to use\nfrom browsers and low-code tools.\n\n**Strictness.** For raw XML bodies, a query parameter that is\nrepeated or not one of `ruleset`, `profile` and `document_type` is\n`400 malformed_request`, so a misspelt selector cannot silently fall\nback to a default. For multipart bodies, exactly one `document`\npart (an XML media type) and at most one `options` part\n(`application/json`) are accepted; a repeated or unknown part is\n`400 malformed_request`. A `Content-Encoding` other than identity is\n`415 unsupported_content_type`.\n\n**Correlation.** Every response carries `X-Ironfang-Request-ID`,\nIronfang's id for the request, which every Problem repeats as\n`request_id`. Send your own `X-Request-ID` and it is echoed back\nunchanged.\n\n**Limits.** The XML is at most 5 MiB (5,242,880 bytes), 64 levels\ndeep and 250,000 elements. Archives and compressed bodies\n(`Content-Encoding` other than identity, ZIP, gzip) are refused with\n`415 unsupported_content_type`; a larger body with\n`413 payload_too_large`. The validation deadline is 10 seconds; a\nvalidation that exceeds it is indeterminate (`504`).\n\n**Ruleset selection.** `ruleset` is either the keyword `latest` or an\nexact immutable ruleset id (`fwrs_...`).\n\n- `latest` resolves to the currently `sendable` release for the\n  document's profile family at the moment of the request. With a\n  `profile`, the result reports `selection_method: latest`; without\n  one, the family is detected from the document's `CustomizationID`\n  and `ProfileID` and the result reports `selection_method:\n  detected`. Detection is for validation only; it is never a\n  statement about what the document should be. A document whose\n  profile cannot be detected - including XML that does not parse -\n  is `422 profile_undetected`; name a `profile` or an exact ruleset\n  to obtain a verdict on it.\n- An exact id validates against that release whatever its state\n  among `scheduled` (migration testing), `sendable` (current) and\n  `historical` (audit and reproduction). The result reports\n  `selection_method: exact` and the release's `state`. An id that is\n  unknown - or names an internal candidate, which is\n  indistinguishable from unknown - is `422 unknown_ruleset`; a\n  withdrawn release is `410 ruleset_disabled`.\n\nThe result always names the resolved immutable id in `ruleset.id`.\n\n**Outcome versus status.** The result separates how far execution\ngot (`status`) from what the standard concluded (`outcome`). A\ndocument that fails the standard at any layer - including malformed\nXML at the `xml` layer - is a successfully processed validation:\nHTTP `200` with `outcome: invalid`, and every deeper layer that could\nnot run is reported as `skipped`, never omitted. Only the request\nitself being wrong, or Financewolf being unable to decide, produces a\nProblem.\n\n**Idempotency.** Authenticated requests may send an\n`Idempotency-Key` (8-128 characters of `A-Z a-z 0-9 _ -`). The key\nis scoped to the organisation and the validation endpoint. Replaying\nthe same request with the same key returns the original operation\nand its result - the same `operation_id`, no second execution, no\nsecond charge - even if `latest` has since come to mean a newer\nrelease. A request is identified by its content: reusing a key with\ndifferent document bytes (a different input SHA-256) or a different\nruleset, profile or document type is `409 idempotency_conflict`.\nRequests without a key always create a new operation. Anonymous\nrequests do not use the header.\n\n**Findings.** At most 1,000 findings are returned inline, in\ndeterministic order: layer order, then severity (`fatal` before\n`warning`), then `rule_id`, then `location`, then `message`.\n`findings_summary` always states how many were returned, how many\nexist, the cap, and whether truncation happened.\n\nSelect your UBL XML file in Body. The file is sent to Financewolf for validation.",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "disabled": true,
                    "key": "Idempotency-Key",
                    "value": "{{Idempotency-Key}}"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/xml"
                  }
                ],
                "method": "POST",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "validate"
                  ],
                  "query": [
                    {
                      "disabled": true,
                      "key": "ruleset",
                      "value": "{{ruleset}}"
                    },
                    {
                      "disabled": true,
                      "key": "profile",
                      "value": "{{profile}}"
                    },
                    {
                      "disabled": true,
                      "key": "document_type",
                      "value": "{{document_type}}"
                    }
                  ],
                  "raw": "{{base_url}}/v1/einvoices/validate"
                }
              },
              "status": "Bad Request"
            },
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"type\": \"https://ironfang.uk/problems/internal_error\",\n  \"title\": \"Internal error\",\n  \"status\": 500,\n  \"detail\": \"Financewolf failed while validating. The document was not validated and nothing was charged.\",\n  \"instance\": \"/financewolf/v1/einvoices/validate\",\n  \"code\": \"internal_error\",\n  \"request_id\": \"req_01J9Q4V7E9YAN0Q7R3S9TWZC6H\",\n  \"outcome\": \"indeterminate\",\n  \"usage_charged\": false\n}",
              "code": 500,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/problem+json"
                }
              ],
              "name": "500 An internal failure during validation",
              "originalRequest": {
                "body": {
                  "file": {
                    "src": ""
                  },
                  "mode": "file"
                },
                "description": "Validates a single UBL Invoice or CreditNote document synchronously and\nreturns a `ValidationVerdict`.\n\n**Body.** Send the raw XML as the request body with\n`Content-Type: application/xml` or `text/xml`, choosing the ruleset\nwith the query parameters. Or send `multipart/form-data` with a\n`document` part holding the XML and an optional `options` part\nholding a JSON `ValidationOptions` object; query parameters are not\nread for multipart requests. The multipart form is the one to use\nfrom browsers and low-code tools.\n\n**Strictness.** For raw XML bodies, a query parameter that is\nrepeated or not one of `ruleset`, `profile` and `document_type` is\n`400 malformed_request`, so a misspelt selector cannot silently fall\nback to a default. For multipart bodies, exactly one `document`\npart (an XML media type) and at most one `options` part\n(`application/json`) are accepted; a repeated or unknown part is\n`400 malformed_request`. A `Content-Encoding` other than identity is\n`415 unsupported_content_type`.\n\n**Correlation.** Every response carries `X-Ironfang-Request-ID`,\nIronfang's id for the request, which every Problem repeats as\n`request_id`. Send your own `X-Request-ID` and it is echoed back\nunchanged.\n\n**Limits.** The XML is at most 5 MiB (5,242,880 bytes), 64 levels\ndeep and 250,000 elements. Archives and compressed bodies\n(`Content-Encoding` other than identity, ZIP, gzip) are refused with\n`415 unsupported_content_type`; a larger body with\n`413 payload_too_large`. The validation deadline is 10 seconds; a\nvalidation that exceeds it is indeterminate (`504`).\n\n**Ruleset selection.** `ruleset` is either the keyword `latest` or an\nexact immutable ruleset id (`fwrs_...`).\n\n- `latest` resolves to the currently `sendable` release for the\n  document's profile family at the moment of the request. With a\n  `profile`, the result reports `selection_method: latest`; without\n  one, the family is detected from the document's `CustomizationID`\n  and `ProfileID` and the result reports `selection_method:\n  detected`. Detection is for validation only; it is never a\n  statement about what the document should be. A document whose\n  profile cannot be detected - including XML that does not parse -\n  is `422 profile_undetected`; name a `profile` or an exact ruleset\n  to obtain a verdict on it.\n- An exact id validates against that release whatever its state\n  among `scheduled` (migration testing), `sendable` (current) and\n  `historical` (audit and reproduction). The result reports\n  `selection_method: exact` and the release's `state`. An id that is\n  unknown - or names an internal candidate, which is\n  indistinguishable from unknown - is `422 unknown_ruleset`; a\n  withdrawn release is `410 ruleset_disabled`.\n\nThe result always names the resolved immutable id in `ruleset.id`.\n\n**Outcome versus status.** The result separates how far execution\ngot (`status`) from what the standard concluded (`outcome`). A\ndocument that fails the standard at any layer - including malformed\nXML at the `xml` layer - is a successfully processed validation:\nHTTP `200` with `outcome: invalid`, and every deeper layer that could\nnot run is reported as `skipped`, never omitted. Only the request\nitself being wrong, or Financewolf being unable to decide, produces a\nProblem.\n\n**Idempotency.** Authenticated requests may send an\n`Idempotency-Key` (8-128 characters of `A-Z a-z 0-9 _ -`). The key\nis scoped to the organisation and the validation endpoint. Replaying\nthe same request with the same key returns the original operation\nand its result - the same `operation_id`, no second execution, no\nsecond charge - even if `latest` has since come to mean a newer\nrelease. A request is identified by its content: reusing a key with\ndifferent document bytes (a different input SHA-256) or a different\nruleset, profile or document type is `409 idempotency_conflict`.\nRequests without a key always create a new operation. Anonymous\nrequests do not use the header.\n\n**Findings.** At most 1,000 findings are returned inline, in\ndeterministic order: layer order, then severity (`fatal` before\n`warning`), then `rule_id`, then `location`, then `message`.\n`findings_summary` always states how many were returned, how many\nexist, the cap, and whether truncation happened.\n\nSelect your UBL XML file in Body. The file is sent to Financewolf for validation.",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "disabled": true,
                    "key": "Idempotency-Key",
                    "value": "{{Idempotency-Key}}"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/xml"
                  }
                ],
                "method": "POST",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "validate"
                  ],
                  "query": [
                    {
                      "disabled": true,
                      "key": "ruleset",
                      "value": "{{ruleset}}"
                    },
                    {
                      "disabled": true,
                      "key": "profile",
                      "value": "{{profile}}"
                    },
                    {
                      "disabled": true,
                      "key": "document_type",
                      "value": "{{document_type}}"
                    }
                  ],
                  "raw": "{{base_url}}/v1/einvoices/validate"
                }
              },
              "status": "Internal Server Error"
            },
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"type\": \"https://ironfang.uk/problems/validator_unavailable\",\n  \"title\": \"Validator unavailable\",\n  \"status\": 503,\n  \"detail\": \"The validation engine did not answer. The document was not validated and nothing was charged; retry shortly.\",\n  \"instance\": \"/financewolf/v1/einvoices/validate\",\n  \"code\": \"validator_unavailable\",\n  \"request_id\": \"req_01J9Q4S0B2VXK7M8N4P6RTWZ3E\",\n  \"operation_id\": \"op_01J9Q4RZ9YT3M2N5P7QSVWX8HD\",\n  \"outcome\": \"indeterminate\",\n  \"usage_charged\": false,\n  \"retry_after_seconds\": 15\n}",
              "code": 503,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/problem+json"
                }
              ],
              "name": "503 Validator dependency failure",
              "originalRequest": {
                "body": {
                  "file": {
                    "src": ""
                  },
                  "mode": "file"
                },
                "description": "Validates a single UBL Invoice or CreditNote document synchronously and\nreturns a `ValidationVerdict`.\n\n**Body.** Send the raw XML as the request body with\n`Content-Type: application/xml` or `text/xml`, choosing the ruleset\nwith the query parameters. Or send `multipart/form-data` with a\n`document` part holding the XML and an optional `options` part\nholding a JSON `ValidationOptions` object; query parameters are not\nread for multipart requests. The multipart form is the one to use\nfrom browsers and low-code tools.\n\n**Strictness.** For raw XML bodies, a query parameter that is\nrepeated or not one of `ruleset`, `profile` and `document_type` is\n`400 malformed_request`, so a misspelt selector cannot silently fall\nback to a default. For multipart bodies, exactly one `document`\npart (an XML media type) and at most one `options` part\n(`application/json`) are accepted; a repeated or unknown part is\n`400 malformed_request`. A `Content-Encoding` other than identity is\n`415 unsupported_content_type`.\n\n**Correlation.** Every response carries `X-Ironfang-Request-ID`,\nIronfang's id for the request, which every Problem repeats as\n`request_id`. Send your own `X-Request-ID` and it is echoed back\nunchanged.\n\n**Limits.** The XML is at most 5 MiB (5,242,880 bytes), 64 levels\ndeep and 250,000 elements. Archives and compressed bodies\n(`Content-Encoding` other than identity, ZIP, gzip) are refused with\n`415 unsupported_content_type`; a larger body with\n`413 payload_too_large`. The validation deadline is 10 seconds; a\nvalidation that exceeds it is indeterminate (`504`).\n\n**Ruleset selection.** `ruleset` is either the keyword `latest` or an\nexact immutable ruleset id (`fwrs_...`).\n\n- `latest` resolves to the currently `sendable` release for the\n  document's profile family at the moment of the request. With a\n  `profile`, the result reports `selection_method: latest`; without\n  one, the family is detected from the document's `CustomizationID`\n  and `ProfileID` and the result reports `selection_method:\n  detected`. Detection is for validation only; it is never a\n  statement about what the document should be. A document whose\n  profile cannot be detected - including XML that does not parse -\n  is `422 profile_undetected`; name a `profile` or an exact ruleset\n  to obtain a verdict on it.\n- An exact id validates against that release whatever its state\n  among `scheduled` (migration testing), `sendable` (current) and\n  `historical` (audit and reproduction). The result reports\n  `selection_method: exact` and the release's `state`. An id that is\n  unknown - or names an internal candidate, which is\n  indistinguishable from unknown - is `422 unknown_ruleset`; a\n  withdrawn release is `410 ruleset_disabled`.\n\nThe result always names the resolved immutable id in `ruleset.id`.\n\n**Outcome versus status.** The result separates how far execution\ngot (`status`) from what the standard concluded (`outcome`). A\ndocument that fails the standard at any layer - including malformed\nXML at the `xml` layer - is a successfully processed validation:\nHTTP `200` with `outcome: invalid`, and every deeper layer that could\nnot run is reported as `skipped`, never omitted. Only the request\nitself being wrong, or Financewolf being unable to decide, produces a\nProblem.\n\n**Idempotency.** Authenticated requests may send an\n`Idempotency-Key` (8-128 characters of `A-Z a-z 0-9 _ -`). The key\nis scoped to the organisation and the validation endpoint. Replaying\nthe same request with the same key returns the original operation\nand its result - the same `operation_id`, no second execution, no\nsecond charge - even if `latest` has since come to mean a newer\nrelease. A request is identified by its content: reusing a key with\ndifferent document bytes (a different input SHA-256) or a different\nruleset, profile or document type is `409 idempotency_conflict`.\nRequests without a key always create a new operation. Anonymous\nrequests do not use the header.\n\n**Findings.** At most 1,000 findings are returned inline, in\ndeterministic order: layer order, then severity (`fatal` before\n`warning`), then `rule_id`, then `location`, then `message`.\n`findings_summary` always states how many were returned, how many\nexist, the cap, and whether truncation happened.\n\nSelect your UBL XML file in Body. The file is sent to Financewolf for validation.",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "disabled": true,
                    "key": "Idempotency-Key",
                    "value": "{{Idempotency-Key}}"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/xml"
                  }
                ],
                "method": "POST",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "validate"
                  ],
                  "query": [
                    {
                      "disabled": true,
                      "key": "ruleset",
                      "value": "{{ruleset}}"
                    },
                    {
                      "disabled": true,
                      "key": "profile",
                      "value": "{{profile}}"
                    },
                    {
                      "disabled": true,
                      "key": "document_type",
                      "value": "{{document_type}}"
                    }
                  ],
                  "raw": "{{base_url}}/v1/einvoices/validate"
                }
              },
              "status": "Service Unavailable"
            },
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"type\": \"https://ironfang.uk/problems/validation_timeout\",\n  \"title\": \"Validation deadline exceeded\",\n  \"status\": 504,\n  \"detail\": \"Validation did not finish within 10 seconds. The document was not validated and nothing was charged.\",\n  \"instance\": \"/financewolf/v1/einvoices/validate\",\n  \"code\": \"validation_timeout\",\n  \"request_id\": \"req_01J9Q4T4C6WYL8N9P5Q7SUXA4F\",\n  \"outcome\": \"indeterminate\",\n  \"usage_charged\": false\n}",
              "code": 504,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/problem+json"
                }
              ],
              "name": "504 Deadline exceeded",
              "originalRequest": {
                "body": {
                  "file": {
                    "src": ""
                  },
                  "mode": "file"
                },
                "description": "Validates a single UBL Invoice or CreditNote document synchronously and\nreturns a `ValidationVerdict`.\n\n**Body.** Send the raw XML as the request body with\n`Content-Type: application/xml` or `text/xml`, choosing the ruleset\nwith the query parameters. Or send `multipart/form-data` with a\n`document` part holding the XML and an optional `options` part\nholding a JSON `ValidationOptions` object; query parameters are not\nread for multipart requests. The multipart form is the one to use\nfrom browsers and low-code tools.\n\n**Strictness.** For raw XML bodies, a query parameter that is\nrepeated or not one of `ruleset`, `profile` and `document_type` is\n`400 malformed_request`, so a misspelt selector cannot silently fall\nback to a default. For multipart bodies, exactly one `document`\npart (an XML media type) and at most one `options` part\n(`application/json`) are accepted; a repeated or unknown part is\n`400 malformed_request`. A `Content-Encoding` other than identity is\n`415 unsupported_content_type`.\n\n**Correlation.** Every response carries `X-Ironfang-Request-ID`,\nIronfang's id for the request, which every Problem repeats as\n`request_id`. Send your own `X-Request-ID` and it is echoed back\nunchanged.\n\n**Limits.** The XML is at most 5 MiB (5,242,880 bytes), 64 levels\ndeep and 250,000 elements. Archives and compressed bodies\n(`Content-Encoding` other than identity, ZIP, gzip) are refused with\n`415 unsupported_content_type`; a larger body with\n`413 payload_too_large`. The validation deadline is 10 seconds; a\nvalidation that exceeds it is indeterminate (`504`).\n\n**Ruleset selection.** `ruleset` is either the keyword `latest` or an\nexact immutable ruleset id (`fwrs_...`).\n\n- `latest` resolves to the currently `sendable` release for the\n  document's profile family at the moment of the request. With a\n  `profile`, the result reports `selection_method: latest`; without\n  one, the family is detected from the document's `CustomizationID`\n  and `ProfileID` and the result reports `selection_method:\n  detected`. Detection is for validation only; it is never a\n  statement about what the document should be. A document whose\n  profile cannot be detected - including XML that does not parse -\n  is `422 profile_undetected`; name a `profile` or an exact ruleset\n  to obtain a verdict on it.\n- An exact id validates against that release whatever its state\n  among `scheduled` (migration testing), `sendable` (current) and\n  `historical` (audit and reproduction). The result reports\n  `selection_method: exact` and the release's `state`. An id that is\n  unknown - or names an internal candidate, which is\n  indistinguishable from unknown - is `422 unknown_ruleset`; a\n  withdrawn release is `410 ruleset_disabled`.\n\nThe result always names the resolved immutable id in `ruleset.id`.\n\n**Outcome versus status.** The result separates how far execution\ngot (`status`) from what the standard concluded (`outcome`). A\ndocument that fails the standard at any layer - including malformed\nXML at the `xml` layer - is a successfully processed validation:\nHTTP `200` with `outcome: invalid`, and every deeper layer that could\nnot run is reported as `skipped`, never omitted. Only the request\nitself being wrong, or Financewolf being unable to decide, produces a\nProblem.\n\n**Idempotency.** Authenticated requests may send an\n`Idempotency-Key` (8-128 characters of `A-Z a-z 0-9 _ -`). The key\nis scoped to the organisation and the validation endpoint. Replaying\nthe same request with the same key returns the original operation\nand its result - the same `operation_id`, no second execution, no\nsecond charge - even if `latest` has since come to mean a newer\nrelease. A request is identified by its content: reusing a key with\ndifferent document bytes (a different input SHA-256) or a different\nruleset, profile or document type is `409 idempotency_conflict`.\nRequests without a key always create a new operation. Anonymous\nrequests do not use the header.\n\n**Findings.** At most 1,000 findings are returned inline, in\ndeterministic order: layer order, then severity (`fatal` before\n`warning`), then `rule_id`, then `location`, then `message`.\n`findings_summary` always states how many were returned, how many\nexist, the cap, and whether truncation happened.\n\nSelect your UBL XML file in Body. The file is sent to Financewolf for validation.",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "disabled": true,
                    "key": "Idempotency-Key",
                    "value": "{{Idempotency-Key}}"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/xml"
                  }
                ],
                "method": "POST",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "validate"
                  ],
                  "query": [
                    {
                      "disabled": true,
                      "key": "ruleset",
                      "value": "{{ruleset}}"
                    },
                    {
                      "disabled": true,
                      "key": "profile",
                      "value": "{{profile}}"
                    },
                    {
                      "disabled": true,
                      "key": "document_type",
                      "value": "{{document_type}}"
                    }
                  ],
                  "raw": "{{base_url}}/v1/einvoices/validate"
                }
              },
              "status": "Gateway Timeout"
            }
          ]
        }
      ],
      "name": "Validation"
    },
    {
      "description": "Deterministic JSON-to-UBL generation, validated before success.",
      "item": [
        {
          "name": "Generate and validate one UBL e-invoice",
          "request": {
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{{generation_input}}"
            },
            "description": "Accepts generation-input/v1 JSON with decimal strings. Both ruleset (latest or an exact immutable ID) and profile are required; document_type comes from the body. Unknown or repeated query parameters, duplicate JSON keys, unknown fields, nulls, JSON numbers and inconsistent calculated totals are refused before acceptance. UTF-8 application/json only; uncompressed body at most 5 MiB.\n\nThe operation pins the selected ruleset, generator/schema versions and engine image at acceptance. Only the exact generated XML bytes validated by that engine can succeed. Every validation layer must pass. Generated XML which fails the rules is an internal 500 generation_failed, indeterminate and uncharged; no artifact or customer-invalid verdict is returned. Dependency failures are 503/504. Validation has a 10-second deadline and result persistence a separate 5-second budget.\n\nAuthenticated requests require financewolf:einvoices:write and optionally Idempotency-Key. The key is scoped to the organisation and generation endpoint. Input identity is financewolf-json/1: UTF-8 JSON decoded and re-encoded with sorted object keys, compact separators and Go JSON string escaping. Whitespace, object key order and equivalent string escapes do not change identity; decimal string spelling and array order do. An existing key replays the original bytes without execution or a second usage event even after latest or the deployed generator changes; different content or selectors produce 409. Anonymous requests cannot supply Idempotency-Key or a tenant header and create no stored operation or usage event.\n\nThe successful response contains base64 of the exact XML, its SHA-256 and a nested validation verdict. One authenticated success records one einvoice.generation usage event. Source JSON is never retained. Authenticated generated XML and validation findings are retained together for 30 days, retrievable/deletable through /v1/einvoices/results/{id}; anonymous results are not retained. Deletion/expiry erases artifacts and findings but preserves the operation and usage identity, so a replay returns 410 and never runs or charges again. Responses use Cache-Control: no-store and the standard request correlation headers.",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "disabled": true,
                "key": "Idempotency-Key",
                "value": "{{Idempotency-Key}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "method": "POST",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "einvoices",
                "generate"
              ],
              "query": [
                {
                  "disabled": false,
                  "key": "ruleset",
                  "value": "{{ruleset}}"
                },
                {
                  "disabled": false,
                  "key": "profile",
                  "value": "{{profile}}"
                }
              ],
              "raw": "{{base_url}}/v1/einvoices/generate?ruleset={{ruleset}}\u0026profile={{profile}}"
            }
          },
          "response": [
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"type\": \"https://ironfang.uk/problems/malformed_request\",\n  \"title\": \"Malformed request\",\n  \"status\": 400,\n  \"detail\": \"One or more options are invalid.\",\n  \"instance\": \"/financewolf/v1/einvoices/validate\",\n  \"code\": \"malformed_request\",\n  \"request_id\": \"req_01J9Q4TPD8XZM9P6Q2R8TVYB5G\",\n  \"errors\": [\n    {\n      \"field\": \"options.document_type\",\n      \"code\": \"invalid_value\",\n      \"message\": \"document_type must be invoice, credit_note or auto.\"\n    },\n    {\n      \"field\": \"options.deadline\",\n      \"code\": \"unknown_field\",\n      \"message\": \"deadline is not an option.\"\n    }\n  ]\n}",
              "code": 400,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/problem+json"
                }
              ],
              "name": "400 Bad option values",
              "originalRequest": {
                "body": {
                  "mode": "raw",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  },
                  "raw": "{{generation_input}}"
                },
                "description": "Accepts generation-input/v1 JSON with decimal strings. Both ruleset (latest or an exact immutable ID) and profile are required; document_type comes from the body. Unknown or repeated query parameters, duplicate JSON keys, unknown fields, nulls, JSON numbers and inconsistent calculated totals are refused before acceptance. UTF-8 application/json only; uncompressed body at most 5 MiB.\n\nThe operation pins the selected ruleset, generator/schema versions and engine image at acceptance. Only the exact generated XML bytes validated by that engine can succeed. Every validation layer must pass. Generated XML which fails the rules is an internal 500 generation_failed, indeterminate and uncharged; no artifact or customer-invalid verdict is returned. Dependency failures are 503/504. Validation has a 10-second deadline and result persistence a separate 5-second budget.\n\nAuthenticated requests require financewolf:einvoices:write and optionally Idempotency-Key. The key is scoped to the organisation and generation endpoint. Input identity is financewolf-json/1: UTF-8 JSON decoded and re-encoded with sorted object keys, compact separators and Go JSON string escaping. Whitespace, object key order and equivalent string escapes do not change identity; decimal string spelling and array order do. An existing key replays the original bytes without execution or a second usage event even after latest or the deployed generator changes; different content or selectors produce 409. Anonymous requests cannot supply Idempotency-Key or a tenant header and create no stored operation or usage event.\n\nThe successful response contains base64 of the exact XML, its SHA-256 and a nested validation verdict. One authenticated success records one einvoice.generation usage event. Source JSON is never retained. Authenticated generated XML and validation findings are retained together for 30 days, retrievable/deletable through /v1/einvoices/results/{id}; anonymous results are not retained. Deletion/expiry erases artifacts and findings but preserves the operation and usage identity, so a replay returns 410 and never runs or charges again. Responses use Cache-Control: no-store and the standard request correlation headers.",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "disabled": true,
                    "key": "Idempotency-Key",
                    "value": "{{Idempotency-Key}}"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "method": "POST",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "generate"
                  ],
                  "query": [
                    {
                      "disabled": false,
                      "key": "ruleset",
                      "value": "{{ruleset}}"
                    },
                    {
                      "disabled": false,
                      "key": "profile",
                      "value": "{{profile}}"
                    }
                  ],
                  "raw": "{{base_url}}/v1/einvoices/generate?ruleset={{ruleset}}\u0026profile={{profile}}"
                }
              },
              "status": "Bad Request"
            },
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"type\": \"https://ironfang.uk/problems/validator_unavailable\",\n  \"title\": \"Validator unavailable\",\n  \"status\": 503,\n  \"detail\": \"The validation engine did not answer. The document was not validated and nothing was charged; retry shortly.\",\n  \"instance\": \"/financewolf/v1/einvoices/validate\",\n  \"code\": \"validator_unavailable\",\n  \"request_id\": \"req_01J9Q4S0B2VXK7M8N4P6RTWZ3E\",\n  \"operation_id\": \"op_01J9Q4RZ9YT3M2N5P7QSVWX8HD\",\n  \"outcome\": \"indeterminate\",\n  \"usage_charged\": false,\n  \"retry_after_seconds\": 15\n}",
              "code": 503,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/problem+json"
                }
              ],
              "name": "503 Validator dependency failure",
              "originalRequest": {
                "body": {
                  "mode": "raw",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  },
                  "raw": "{{generation_input}}"
                },
                "description": "Accepts generation-input/v1 JSON with decimal strings. Both ruleset (latest or an exact immutable ID) and profile are required; document_type comes from the body. Unknown or repeated query parameters, duplicate JSON keys, unknown fields, nulls, JSON numbers and inconsistent calculated totals are refused before acceptance. UTF-8 application/json only; uncompressed body at most 5 MiB.\n\nThe operation pins the selected ruleset, generator/schema versions and engine image at acceptance. Only the exact generated XML bytes validated by that engine can succeed. Every validation layer must pass. Generated XML which fails the rules is an internal 500 generation_failed, indeterminate and uncharged; no artifact or customer-invalid verdict is returned. Dependency failures are 503/504. Validation has a 10-second deadline and result persistence a separate 5-second budget.\n\nAuthenticated requests require financewolf:einvoices:write and optionally Idempotency-Key. The key is scoped to the organisation and generation endpoint. Input identity is financewolf-json/1: UTF-8 JSON decoded and re-encoded with sorted object keys, compact separators and Go JSON string escaping. Whitespace, object key order and equivalent string escapes do not change identity; decimal string spelling and array order do. An existing key replays the original bytes without execution or a second usage event even after latest or the deployed generator changes; different content or selectors produce 409. Anonymous requests cannot supply Idempotency-Key or a tenant header and create no stored operation or usage event.\n\nThe successful response contains base64 of the exact XML, its SHA-256 and a nested validation verdict. One authenticated success records one einvoice.generation usage event. Source JSON is never retained. Authenticated generated XML and validation findings are retained together for 30 days, retrievable/deletable through /v1/einvoices/results/{id}; anonymous results are not retained. Deletion/expiry erases artifacts and findings but preserves the operation and usage identity, so a replay returns 410 and never runs or charges again. Responses use Cache-Control: no-store and the standard request correlation headers.",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "disabled": true,
                    "key": "Idempotency-Key",
                    "value": "{{Idempotency-Key}}"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "method": "POST",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "generate"
                  ],
                  "query": [
                    {
                      "disabled": false,
                      "key": "ruleset",
                      "value": "{{ruleset}}"
                    },
                    {
                      "disabled": false,
                      "key": "profile",
                      "value": "{{profile}}"
                    }
                  ],
                  "raw": "{{base_url}}/v1/einvoices/generate?ruleset={{ruleset}}\u0026profile={{profile}}"
                }
              },
              "status": "Service Unavailable"
            },
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"type\": \"https://ironfang.uk/problems/validation_timeout\",\n  \"title\": \"Validation deadline exceeded\",\n  \"status\": 504,\n  \"detail\": \"Validation did not finish within 10 seconds. The document was not validated and nothing was charged.\",\n  \"instance\": \"/financewolf/v1/einvoices/validate\",\n  \"code\": \"validation_timeout\",\n  \"request_id\": \"req_01J9Q4T4C6WYL8N9P5Q7SUXA4F\",\n  \"outcome\": \"indeterminate\",\n  \"usage_charged\": false\n}",
              "code": 504,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/problem+json"
                }
              ],
              "name": "504 Deadline exceeded",
              "originalRequest": {
                "body": {
                  "mode": "raw",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  },
                  "raw": "{{generation_input}}"
                },
                "description": "Accepts generation-input/v1 JSON with decimal strings. Both ruleset (latest or an exact immutable ID) and profile are required; document_type comes from the body. Unknown or repeated query parameters, duplicate JSON keys, unknown fields, nulls, JSON numbers and inconsistent calculated totals are refused before acceptance. UTF-8 application/json only; uncompressed body at most 5 MiB.\n\nThe operation pins the selected ruleset, generator/schema versions and engine image at acceptance. Only the exact generated XML bytes validated by that engine can succeed. Every validation layer must pass. Generated XML which fails the rules is an internal 500 generation_failed, indeterminate and uncharged; no artifact or customer-invalid verdict is returned. Dependency failures are 503/504. Validation has a 10-second deadline and result persistence a separate 5-second budget.\n\nAuthenticated requests require financewolf:einvoices:write and optionally Idempotency-Key. The key is scoped to the organisation and generation endpoint. Input identity is financewolf-json/1: UTF-8 JSON decoded and re-encoded with sorted object keys, compact separators and Go JSON string escaping. Whitespace, object key order and equivalent string escapes do not change identity; decimal string spelling and array order do. An existing key replays the original bytes without execution or a second usage event even after latest or the deployed generator changes; different content or selectors produce 409. Anonymous requests cannot supply Idempotency-Key or a tenant header and create no stored operation or usage event.\n\nThe successful response contains base64 of the exact XML, its SHA-256 and a nested validation verdict. One authenticated success records one einvoice.generation usage event. Source JSON is never retained. Authenticated generated XML and validation findings are retained together for 30 days, retrievable/deletable through /v1/einvoices/results/{id}; anonymous results are not retained. Deletion/expiry erases artifacts and findings but preserves the operation and usage identity, so a replay returns 410 and never runs or charges again. Responses use Cache-Control: no-store and the standard request correlation headers.",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "disabled": true,
                    "key": "Idempotency-Key",
                    "value": "{{Idempotency-Key}}"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "method": "POST",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "generate"
                  ],
                  "query": [
                    {
                      "disabled": false,
                      "key": "ruleset",
                      "value": "{{ruleset}}"
                    },
                    {
                      "disabled": false,
                      "key": "profile",
                      "value": "{{profile}}"
                    }
                  ],
                  "raw": "{{base_url}}/v1/einvoices/generate?ruleset={{ruleset}}\u0026profile={{profile}}"
                }
              },
              "status": "Gateway Timeout"
            }
          ]
        }
      ],
      "name": "Generation"
    },
    {
      "description": "The immutable ruleset registry validation results are pinned to.",
      "item": [
        {
          "name": "List rulesets available for selection and reproduction",
          "request": {
            "description": "Lists the immutable rulesets a caller may validate against:\n`scheduled`, `sendable` and `historical` releases. Candidate releases\nare internal and never listed; `disabled` releases are withdrawn and\nnever appear in a successful body - their ids answer `410` on the\nexact read.\n\nExactly one `sendable` release per profile family and document type\ncarries `latest: true`: that is what `ruleset: latest` resolves to for\na request made now. `scheduled` releases show what `latest` will\nbecome and when, and may be validated against by exact id for\nmigration testing; `historical` releases stay retrievable and\nvalidatable by exact id so an older result can be reproduced.\n",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "einvoices",
                "rulesets"
              ],
              "query": [
                {
                  "disabled": true,
                  "key": "document_type",
                  "value": "{{document_type}}"
                },
                {
                  "disabled": true,
                  "key": "profile",
                  "value": "{{profile}}"
                },
                {
                  "disabled": true,
                  "key": "state",
                  "value": "{{state}}"
                }
              ],
              "raw": "{{base_url}}/v1/einvoices/rulesets"
            }
          },
          "response": [
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"schema\": \"financewolf/einvoice/ruleset-list/v1\",\n  \"rulesets\": [\n    {\n      \"schema\": \"financewolf/einvoice/ruleset/v1\",\n      \"id\": \"fwrs_example_invoice_2026b\",\n      \"state\": \"scheduled\",\n      \"latest\": false,\n      \"profile_family\": \"peppol-bis-billing-3\",\n      \"document_type\": \"invoice\",\n      \"syntax\": \"UBL 2.1\",\n      \"official_release\": \"Example release 2026b\",\n      \"en16931_release\": \"0.0.1-example\",\n      \"bis_release\": \"0.0.1-example\",\n      \"vesid\": \"uk.ironfang.example:invoice:2026b\",\n      \"engine\": {\n        \"name\": \"phive/12.1.0\",\n        \"image_digest\": \"sha256:4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f\"\n      },\n      \"valid_from\": \"2027-02-15T00:00:00Z\",\n      \"valid_until\": null,\n      \"sources\": [\n        \"https://example.test/releases/2026b/notes\",\n        \"https://example.test/releases/2026b/download\"\n      ],\n      \"artifacts\": [\n        {\n          \"layer\": \"xsd\",\n          \"sha256\": \"1111111111111111111111111111111111111111111111111111111111111111\",\n          \"licence\": \"NOASSERTION\",\n          \"licence_status\": \"no-raw-redistribution\",\n          \"source_url\": \"https://example.test/artefacts/2026b/xsd\"\n        },\n        {\n          \"layer\": \"en16931\",\n          \"sha256\": \"2222222222222222222222222222222222222222222222222222222222222222\",\n          \"licence\": \"EUPL-1.2\",\n          \"licence_status\": \"notice-required\",\n          \"source_url\": \"https://example.test/artefacts/2026b/en16931\"\n        },\n        {\n          \"layer\": \"peppol\",\n          \"sha256\": \"3333333333333333333333333333333333333333333333333333333333333333\",\n          \"licence\": \"NOASSERTION\",\n          \"licence_status\": \"no-raw-redistribution\",\n          \"source_url\": \"https://example.test/artefacts/2026b/peppol\"\n        }\n      ]\n    },\n    {\n      \"schema\": \"financewolf/einvoice/ruleset/v1\",\n      \"id\": \"fwrs_example_invoice_2026a\",\n      \"state\": \"sendable\",\n      \"latest\": true,\n      \"profile_family\": \"peppol-bis-billing-3\",\n      \"document_type\": \"invoice\",\n      \"syntax\": \"UBL 2.1\",\n      \"official_release\": \"Example release 2026a\",\n      \"en16931_release\": \"0.0.1-example\",\n      \"bis_release\": \"0.0.1-example\",\n      \"vesid\": \"uk.ironfang.example:invoice:2026a\",\n      \"engine\": {\n        \"name\": \"phive/12.1.0\",\n        \"image_digest\": \"sha256:4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f\"\n      },\n      \"valid_from\": \"2026-08-17T00:00:00Z\",\n      \"valid_until\": null,\n      \"published_on\": \"2026-05-14\",\n      \"sources\": [\n        \"https://example.test/releases/2026a/notes\",\n        \"https://example.test/releases/2026a/download\"\n      ],\n      \"artifacts\": [\n        {\n          \"layer\": \"xsd\",\n          \"sha256\": \"1111111111111111111111111111111111111111111111111111111111111111\",\n          \"licence\": \"NOASSERTION\",\n          \"licence_status\": \"no-raw-redistribution\",\n          \"source_url\": \"https://example.test/artefacts/2026a/xsd\"\n        },\n        {\n          \"layer\": \"en16931\",\n          \"sha256\": \"2222222222222222222222222222222222222222222222222222222222222222\",\n          \"licence\": \"EUPL-1.2\",\n          \"licence_status\": \"notice-required\",\n          \"source_url\": \"https://example.test/artefacts/2026a/en16931\"\n        },\n        {\n          \"layer\": \"peppol\",\n          \"sha256\": \"3333333333333333333333333333333333333333333333333333333333333333\",\n          \"licence\": \"NOASSERTION\",\n          \"licence_status\": \"no-raw-redistribution\",\n          \"source_url\": \"https://example.test/artefacts/2026a/peppol\"\n        }\n      ]\n    },\n    {\n      \"schema\": \"financewolf/einvoice/ruleset/v1\",\n      \"id\": \"fwrs_example_invoice_2025z\",\n      \"state\": \"historical\",\n      \"latest\": false,\n      \"profile_family\": \"peppol-bis-billing-3\",\n      \"document_type\": \"invoice\",\n      \"syntax\": \"UBL 2.1\",\n      \"official_release\": \"Example release 2025z\",\n      \"en16931_release\": \"0.0.1-example\",\n      \"bis_release\": \"0.0.1-example\",\n      \"vesid\": \"uk.ironfang.example:invoice:2025z\",\n      \"engine\": {\n        \"name\": \"phive/12.1.0\",\n        \"image_digest\": \"sha256:4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f\"\n      },\n      \"valid_from\": \"2026-02-16T00:00:00Z\",\n      \"valid_until\": \"2026-08-17T00:00:00Z\",\n      \"retired_at\": \"2026-08-17T00:00:00Z\",\n      \"replaced_by\": \"fwrs_example_invoice_2026a\",\n      \"sources\": [\n        \"https://example.test/releases/2025z/notes\",\n        \"https://example.test/releases/2025z/download\"\n      ],\n      \"artifacts\": [\n        {\n          \"layer\": \"xsd\",\n          \"sha256\": \"1111111111111111111111111111111111111111111111111111111111111111\",\n          \"licence\": \"NOASSERTION\",\n          \"licence_status\": \"no-raw-redistribution\",\n          \"source_url\": \"https://example.test/artefacts/2025z/xsd\"\n        },\n        {\n          \"layer\": \"en16931\",\n          \"sha256\": \"2222222222222222222222222222222222222222222222222222222222222222\",\n          \"licence\": \"EUPL-1.2\",\n          \"licence_status\": \"notice-required\",\n          \"source_url\": \"https://example.test/artefacts/2025z/en16931\"\n        },\n        {\n          \"layer\": \"peppol\",\n          \"sha256\": \"3333333333333333333333333333333333333333333333333333333333333333\",\n          \"licence\": \"NOASSERTION\",\n          \"licence_status\": \"no-raw-redistribution\",\n          \"source_url\": \"https://example.test/artefacts/2025z/peppol\"\n        }\n      ]\n    }\n  ]\n}",
              "code": 200,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "name": "200 Upcoming, current and past releases for invoices",
              "originalRequest": {
                "description": "Lists the immutable rulesets a caller may validate against:\n`scheduled`, `sendable` and `historical` releases. Candidate releases\nare internal and never listed; `disabled` releases are withdrawn and\nnever appear in a successful body - their ids answer `410` on the\nexact read.\n\nExactly one `sendable` release per profile family and document type\ncarries `latest: true`: that is what `ruleset: latest` resolves to for\na request made now. `scheduled` releases show what `latest` will\nbecome and when, and may be validated against by exact id for\nmigration testing; `historical` releases stay retrievable and\nvalidatable by exact id so an older result can be reproduced.\n",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "method": "GET",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "rulesets"
                  ],
                  "query": [
                    {
                      "disabled": true,
                      "key": "document_type",
                      "value": "{{document_type}}"
                    },
                    {
                      "disabled": true,
                      "key": "profile",
                      "value": "{{profile}}"
                    },
                    {
                      "disabled": true,
                      "key": "state",
                      "value": "{{state}}"
                    }
                  ],
                  "raw": "{{base_url}}/v1/einvoices/rulesets"
                }
              },
              "status": "OK"
            },
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"type\": \"https://ironfang.uk/problems/malformed_request\",\n  \"title\": \"Malformed request\",\n  \"status\": 400,\n  \"detail\": \"One or more options are invalid.\",\n  \"instance\": \"/financewolf/v1/einvoices/validate\",\n  \"code\": \"malformed_request\",\n  \"request_id\": \"req_01J9Q4TPD8XZM9P6Q2R8TVYB5G\",\n  \"errors\": [\n    {\n      \"field\": \"options.document_type\",\n      \"code\": \"invalid_value\",\n      \"message\": \"document_type must be invoice, credit_note or auto.\"\n    },\n    {\n      \"field\": \"options.deadline\",\n      \"code\": \"unknown_field\",\n      \"message\": \"deadline is not an option.\"\n    }\n  ]\n}",
              "code": 400,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/problem+json"
                }
              ],
              "name": "400 Bad option values",
              "originalRequest": {
                "description": "Lists the immutable rulesets a caller may validate against:\n`scheduled`, `sendable` and `historical` releases. Candidate releases\nare internal and never listed; `disabled` releases are withdrawn and\nnever appear in a successful body - their ids answer `410` on the\nexact read.\n\nExactly one `sendable` release per profile family and document type\ncarries `latest: true`: that is what `ruleset: latest` resolves to for\na request made now. `scheduled` releases show what `latest` will\nbecome and when, and may be validated against by exact id for\nmigration testing; `historical` releases stay retrievable and\nvalidatable by exact id so an older result can be reproduced.\n",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "method": "GET",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "rulesets"
                  ],
                  "query": [
                    {
                      "disabled": true,
                      "key": "document_type",
                      "value": "{{document_type}}"
                    },
                    {
                      "disabled": true,
                      "key": "profile",
                      "value": "{{profile}}"
                    },
                    {
                      "disabled": true,
                      "key": "state",
                      "value": "{{state}}"
                    }
                  ],
                  "raw": "{{base_url}}/v1/einvoices/rulesets"
                }
              },
              "status": "Bad Request"
            }
          ]
        },
        {
          "name": "Fetch one ruleset by its immutable id",
          "request": {
            "description": "Returns the customer-safe identity of one ruleset: enough to select\nit, to know its state, and to reproduce a validation that named it.\n`scheduled`, `sendable` and `historical` releases are returned in\nfull. A `disabled` release answers `410 ruleset_disabled`: its id is\nacknowledged, its content is withdrawn, and it never appears in a\nsuccessful body. `latest` is a selection keyword, not an id:\n`GET .../rulesets/latest` is `404`. Candidate releases are internal\nand indistinguishable from unknown: `404`.\n",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "einvoices",
                "rulesets",
                "{{id}}"
              ],
              "query": [],
              "raw": "{{base_url}}/v1/einvoices/rulesets/{{id}}"
            }
          },
          "response": [
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"schema\": \"financewolf/einvoice/ruleset/v1\",\n  \"id\": \"fwrs_example_invoice_2025z\",\n  \"state\": \"historical\",\n  \"latest\": false,\n  \"profile_family\": \"peppol-bis-billing-3\",\n  \"document_type\": \"invoice\",\n  \"syntax\": \"UBL 2.1\",\n  \"official_release\": \"Example release 2025z\",\n  \"en16931_release\": \"0.0.1-example\",\n  \"bis_release\": \"0.0.1-example\",\n  \"vesid\": \"uk.ironfang.example:invoice:2025z\",\n  \"engine\": {\n    \"name\": \"phive/12.1.0\",\n    \"image_digest\": \"sha256:4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f4f\"\n  },\n  \"valid_from\": \"2026-02-16T00:00:00Z\",\n  \"valid_until\": \"2026-08-17T00:00:00Z\",\n  \"retired_at\": \"2026-08-17T00:00:00Z\",\n  \"replaced_by\": \"fwrs_example_invoice_2026a\",\n  \"sources\": [\n    \"https://example.test/releases/2025z/notes\",\n    \"https://example.test/releases/2025z/download\"\n  ],\n  \"artifacts\": [\n    {\n      \"layer\": \"xsd\",\n      \"sha256\": \"1111111111111111111111111111111111111111111111111111111111111111\",\n      \"licence\": \"NOASSERTION\",\n      \"licence_status\": \"no-raw-redistribution\",\n      \"source_url\": \"https://example.test/artefacts/2025z/xsd\"\n    },\n    {\n      \"layer\": \"en16931\",\n      \"sha256\": \"2222222222222222222222222222222222222222222222222222222222222222\",\n      \"licence\": \"EUPL-1.2\",\n      \"licence_status\": \"notice-required\",\n      \"source_url\": \"https://example.test/artefacts/2025z/en16931\"\n    },\n    {\n      \"layer\": \"peppol\",\n      \"sha256\": \"3333333333333333333333333333333333333333333333333333333333333333\",\n      \"licence\": \"NOASSERTION\",\n      \"licence_status\": \"no-raw-redistribution\",\n      \"source_url\": \"https://example.test/artefacts/2025z/peppol\"\n    }\n  ]\n}",
              "code": 200,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "name": "200 An exact historical release, still reproducible",
              "originalRequest": {
                "description": "Returns the customer-safe identity of one ruleset: enough to select\nit, to know its state, and to reproduce a validation that named it.\n`scheduled`, `sendable` and `historical` releases are returned in\nfull. A `disabled` release answers `410 ruleset_disabled`: its id is\nacknowledged, its content is withdrawn, and it never appears in a\nsuccessful body. `latest` is a selection keyword, not an id:\n`GET .../rulesets/latest` is `404`. Candidate releases are internal\nand indistinguishable from unknown: `404`.\n",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "method": "GET",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "rulesets",
                    "{{id}}"
                  ],
                  "query": [],
                  "raw": "{{base_url}}/v1/einvoices/rulesets/{{id}}"
                }
              },
              "status": "OK"
            }
          ]
        }
      ],
      "name": "Rulesets"
    },
    {
      "description": "Authenticated retained validation and generation results.",
      "item": [
        {
          "name": "List saved validation results",
          "request": {
            "description": "Returns up to 20 results, newest first. Use next_cursor as before for another page. Available is false once expired or deleted. Requires financewolf:einvoices:read.",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "einvoices",
                "results"
              ],
              "query": [
                {
                  "disabled": true,
                  "key": "before",
                  "value": "{{before}}"
                }
              ],
              "raw": "{{base_url}}/v1/einvoices/results"
            }
          },
          "response": [
            {
              "_postman_previewlanguage": "json",
              "body": "{\"results\":[],\"retention_days\":30}",
              "code": 200,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "name": "200 ",
              "originalRequest": {
                "description": "Returns up to 20 results, newest first. Use next_cursor as before for another page. Available is false once expired or deleted. Requires financewolf:einvoices:read.",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "method": "GET",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "results"
                  ],
                  "query": [
                    {
                      "disabled": true,
                      "key": "before",
                      "value": "{{before}}"
                    }
                  ],
                  "raw": "{{base_url}}/v1/einvoices/results"
                }
              },
              "status": "OK"
            },
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"type\": \"https://ironfang.uk/problems/malformed_request\",\n  \"title\": \"Malformed request\",\n  \"status\": 400,\n  \"detail\": \"One or more options are invalid.\",\n  \"instance\": \"/financewolf/v1/einvoices/validate\",\n  \"code\": \"malformed_request\",\n  \"request_id\": \"req_01J9Q4TPD8XZM9P6Q2R8TVYB5G\",\n  \"errors\": [\n    {\n      \"field\": \"options.document_type\",\n      \"code\": \"invalid_value\",\n      \"message\": \"document_type must be invoice, credit_note or auto.\"\n    },\n    {\n      \"field\": \"options.deadline\",\n      \"code\": \"unknown_field\",\n      \"message\": \"deadline is not an option.\"\n    }\n  ]\n}",
              "code": 400,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/problem+json"
                }
              ],
              "name": "400 Bad option values",
              "originalRequest": {
                "description": "Returns up to 20 results, newest first. Use next_cursor as before for another page. Available is false once expired or deleted. Requires financewolf:einvoices:read.",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "method": "GET",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "results"
                  ],
                  "query": [
                    {
                      "disabled": true,
                      "key": "before",
                      "value": "{{before}}"
                    }
                  ],
                  "raw": "{{base_url}}/v1/einvoices/results"
                }
              },
              "status": "Bad Request"
            }
          ]
        },
        {
          "name": "Read a saved validation or generation result",
          "request": {
            "description": "Returns the exact original JSON while retained. Unknown or foreign IDs answer 404; expired/deleted results answer 410. Requires financewolf:einvoices:read.",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "einvoices",
                "results",
                "{{id}}"
              ],
              "query": [],
              "raw": "{{base_url}}/v1/einvoices/results/{{id}}"
            }
          },
          "response": [
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"type\": \"https://ironfang.uk/problems/malformed_request\",\n  \"title\": \"Malformed request\",\n  \"status\": 400,\n  \"detail\": \"One or more options are invalid.\",\n  \"instance\": \"/financewolf/v1/einvoices/validate\",\n  \"code\": \"malformed_request\",\n  \"request_id\": \"req_01J9Q4TPD8XZM9P6Q2R8TVYB5G\",\n  \"errors\": [\n    {\n      \"field\": \"options.document_type\",\n      \"code\": \"invalid_value\",\n      \"message\": \"document_type must be invoice, credit_note or auto.\"\n    },\n    {\n      \"field\": \"options.deadline\",\n      \"code\": \"unknown_field\",\n      \"message\": \"deadline is not an option.\"\n    }\n  ]\n}",
              "code": 400,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/problem+json"
                }
              ],
              "name": "400 Bad option values",
              "originalRequest": {
                "description": "Returns the exact original JSON while retained. Unknown or foreign IDs answer 404; expired/deleted results answer 410. Requires financewolf:einvoices:read.",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "method": "GET",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "results",
                    "{{id}}"
                  ],
                  "query": [],
                  "raw": "{{base_url}}/v1/einvoices/results/{{id}}"
                }
              },
              "status": "Bad Request"
            }
          ]
        },
        {
          "name": "Delete saved result bytes and findings",
          "request": {
            "description": "Requires financewolf:einvoices:write. Erases response and findings atomically. Repeated deletion returns 204. Operation/usage metadata and idempotency hashes remain; replay will never repeat the validation. Unknown or foreign IDs answer 404.",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "method": "DELETE",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "einvoices",
                "results",
                "{{id}}"
              ],
              "query": [],
              "raw": "{{base_url}}/v1/einvoices/results/{{id}}"
            }
          },
          "response": [
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"type\": \"https://ironfang.uk/problems/malformed_request\",\n  \"title\": \"Malformed request\",\n  \"status\": 400,\n  \"detail\": \"One or more options are invalid.\",\n  \"instance\": \"/financewolf/v1/einvoices/validate\",\n  \"code\": \"malformed_request\",\n  \"request_id\": \"req_01J9Q4TPD8XZM9P6Q2R8TVYB5G\",\n  \"errors\": [\n    {\n      \"field\": \"options.document_type\",\n      \"code\": \"invalid_value\",\n      \"message\": \"document_type must be invoice, credit_note or auto.\"\n    },\n    {\n      \"field\": \"options.deadline\",\n      \"code\": \"unknown_field\",\n      \"message\": \"deadline is not an option.\"\n    }\n  ]\n}",
              "code": 400,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/problem+json"
                }
              ],
              "name": "400 Bad option values",
              "originalRequest": {
                "description": "Requires financewolf:einvoices:write. Erases response and findings atomically. Repeated deletion returns 204. Operation/usage metadata and idempotency hashes remain; replay will never repeat the validation. Unknown or foreign IDs answer 404.",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "method": "DELETE",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "results",
                    "{{id}}"
                  ],
                  "query": [],
                  "raw": "{{base_url}}/v1/einvoices/results/{{id}}"
                }
              },
              "status": "Bad Request"
            }
          ]
        }
      ],
      "name": "Results"
    },
    {
      "description": "Signed validation observations and independent report integrity verification.",
      "item": [
        {
          "name": "Download a signed report for a retained result",
          "request": {
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\"operation_id\":\"{{operation_id}}\"}"
            },
            "description": "Requires financewolf:einvoices:read and a tenant-owned, retained completed\nvalidation or generation result with all findings present. Truncated\nfindings return 422 report_incomplete. Signing is separately configured;\nunavailable signing or rendering returns 503 report_unavailable.\nSend operation_id and optional include_pdf in a JSON body of at most 1024\nbytes. Query parameters, compression and Idempotency-Key are rejected.\nThe ZIP includes a canonical manifest, domain-separated Ed25519 signature,\nexact saved result, complete findings and a readable HTML report. Generated\nXML is included when retained; include_pdf also binds a fresh readable PDF\nand its template version. Validation-only results cannot include a PDF.\nOptional RFC 3161 timestamps are signed as required when configured.\nThere is no new validation, usage charge or hosted report retention.\nIssuance time/signatures may change on repeated downloads; original facts\nstay pinned. Expiry/deletion is checked again before releasing the bundle.\nIssuance has a 45-second deadline, two slots per API process and a 48 MiB\ncompressed/expanded bundle limit. A signature does not certify compliance.\n",
            "header": [
              {
                "key": "Accept",
                "value": "application/zip"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "method": "POST",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "einvoices",
                "reports"
              ],
              "query": [],
              "raw": "{{base_url}}/v1/einvoices/reports"
            }
          },
          "response": [
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"type\": \"https://ironfang.uk/problems/malformed_request\",\n  \"title\": \"Malformed request\",\n  \"status\": 400,\n  \"detail\": \"One or more options are invalid.\",\n  \"instance\": \"/financewolf/v1/einvoices/validate\",\n  \"code\": \"malformed_request\",\n  \"request_id\": \"req_01J9Q4TPD8XZM9P6Q2R8TVYB5G\",\n  \"errors\": [\n    {\n      \"field\": \"options.document_type\",\n      \"code\": \"invalid_value\",\n      \"message\": \"document_type must be invoice, credit_note or auto.\"\n    },\n    {\n      \"field\": \"options.deadline\",\n      \"code\": \"unknown_field\",\n      \"message\": \"deadline is not an option.\"\n    }\n  ]\n}",
              "code": 400,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/problem+json"
                }
              ],
              "name": "400 Bad option values",
              "originalRequest": {
                "body": {
                  "mode": "raw",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  },
                  "raw": "{\"operation_id\":\"{{operation_id}}\"}"
                },
                "description": "Requires financewolf:einvoices:read and a tenant-owned, retained completed\nvalidation or generation result with all findings present. Truncated\nfindings return 422 report_incomplete. Signing is separately configured;\nunavailable signing or rendering returns 503 report_unavailable.\nSend operation_id and optional include_pdf in a JSON body of at most 1024\nbytes. Query parameters, compression and Idempotency-Key are rejected.\nThe ZIP includes a canonical manifest, domain-separated Ed25519 signature,\nexact saved result, complete findings and a readable HTML report. Generated\nXML is included when retained; include_pdf also binds a fresh readable PDF\nand its template version. Validation-only results cannot include a PDF.\nOptional RFC 3161 timestamps are signed as required when configured.\nThere is no new validation, usage charge or hosted report retention.\nIssuance time/signatures may change on repeated downloads; original facts\nstay pinned. Expiry/deletion is checked again before releasing the bundle.\nIssuance has a 45-second deadline, two slots per API process and a 48 MiB\ncompressed/expanded bundle limit. A signature does not certify compliance.\n",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/zip"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "method": "POST",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "reports"
                  ],
                  "query": [],
                  "raw": "{{base_url}}/v1/einvoices/reports"
                }
              },
              "status": "Bad Request"
            }
          ]
        },
        {
          "name": "Read Financewolf report signing public keys",
          "request": {
            "auth": {
              "type": "noauth"
            },
            "description": "Operator-configured Ed25519 public keys only. Active keys sign new reports;\nretired keys verify old reports; revoked keys are refused. An empty list\nmeans no trust keys are configured. Cache lifetime is 300 seconds. Offline\nverifiers must obtain this document through an independently trusted channel\nand maintain revocation updates. Never trust a key supplied by a report.\n",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "einvoices",
                "reports",
                "keys"
              ],
              "query": [],
              "raw": "{{base_url}}/v1/einvoices/reports/keys"
            }
          },
          "response": []
        },
        {
          "name": "Verify report integrity without an account",
          "request": {
            "auth": {
              "type": "noauth"
            },
            "body": {
              "file": {
                "src": ""
              },
              "mode": "file"
            },
            "description": "Send the ZIP as application/zip. No API key or subscription is needed.\nFinancewolf verifies the canonical manifest, domain-separated signature\nagainst operator-controlled public keys, every bundled artifact hash and\nthe optional timestamp against configured TSA trust roots. It performs\nno network access, no PHIVE validation and no upload retention. For a\ncompletely local check use financewolf-verify with independent trust keys.\nHTTP 200 contains verified true or false. Verified means report integrity,\nnot a valid invoice; inspect invoice_outcome separately. Unknown or revoked\nkeys, tampering, missing timestamp trust and unsupported schemas fail.\nRequests are bounded to 48 MiB compressed and expanded, nine allowed ZIP\nmembers, two concurrent checks per API process and a 15-second deadline.\nNo query, compression or Idempotency-Key is accepted. Per-address anonymous\nlimits apply. Failure details contain no invoice content.\n",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/zip"
              }
            ],
            "method": "POST",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "einvoices",
                "reports",
                "verify"
              ],
              "query": [],
              "raw": "{{base_url}}/v1/einvoices/reports/verify"
            }
          },
          "response": [
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"type\": \"https://ironfang.uk/problems/malformed_request\",\n  \"title\": \"Malformed request\",\n  \"status\": 400,\n  \"detail\": \"One or more options are invalid.\",\n  \"instance\": \"/financewolf/v1/einvoices/validate\",\n  \"code\": \"malformed_request\",\n  \"request_id\": \"req_01J9Q4TPD8XZM9P6Q2R8TVYB5G\",\n  \"errors\": [\n    {\n      \"field\": \"options.document_type\",\n      \"code\": \"invalid_value\",\n      \"message\": \"document_type must be invoice, credit_note or auto.\"\n    },\n    {\n      \"field\": \"options.deadline\",\n      \"code\": \"unknown_field\",\n      \"message\": \"deadline is not an option.\"\n    }\n  ]\n}",
              "code": 400,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/problem+json"
                }
              ],
              "name": "400 Bad option values",
              "originalRequest": {
                "auth": {
                  "type": "noauth"
                },
                "body": {
                  "file": {
                    "src": ""
                  },
                  "mode": "file"
                },
                "description": "Send the ZIP as application/zip. No API key or subscription is needed.\nFinancewolf verifies the canonical manifest, domain-separated signature\nagainst operator-controlled public keys, every bundled artifact hash and\nthe optional timestamp against configured TSA trust roots. It performs\nno network access, no PHIVE validation and no upload retention. For a\ncompletely local check use financewolf-verify with independent trust keys.\nHTTP 200 contains verified true or false. Verified means report integrity,\nnot a valid invoice; inspect invoice_outcome separately. Unknown or revoked\nkeys, tampering, missing timestamp trust and unsupported schemas fail.\nRequests are bounded to 48 MiB compressed and expanded, nine allowed ZIP\nmembers, two concurrent checks per API process and a 15-second deadline.\nNo query, compression or Idempotency-Key is accepted. Per-address anonymous\nlimits apply. Failure details contain no invoice content.\n",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/zip"
                  }
                ],
                "method": "POST",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "reports",
                    "verify"
                  ],
                  "query": [],
                  "raw": "{{base_url}}/v1/einvoices/reports/verify"
                }
              },
              "status": "Bad Request"
            }
          ]
        }
      ],
      "name": "Reports"
    },
    {
      "description": "Safe readable PDFs of retained successfully validated generation results.",
      "item": [
        {
          "name": "Render a saved generation as a readable PDF",
          "request": {
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\"operation_id\":\"{{operation_id}}\"}"
            },
            "description": "Requires financewolf:einvoices:read and a tenant-owned, unexpired successful\ngeneration operation. Supply only operation_id in a JSON body of at most\n1024 bytes. Validation-only and anonymous results cannot be rendered.\nUnknown or foreign IDs return 404; expired/deleted results return 410.\nQuery parameters, Idempotency-Key, customer HTML, URLs and render options\nare not accepted. The source XML and PHIVE verdict are checked before\nrendering; no new validation, operation, usage charge or PDF retention occurs.\nThe versioned template preserves XML values, labels the PDF as a readable\nrepresentation, and includes the source SHA-256 and original ruleset.\nA PDF is not a new validation verdict or a compliance certificate.\nAt most two render requests prepare source data per API process; excess\nrequests return 429 with Retry-After. Rendering has a 30-second deadline\nand a 32 MiB output limit; failure\nleaves the retained result and its original charge unchanged. PDF bytes\nmay vary across Chromium builds and request times. Responses are no-store.\n",
            "header": [
              {
                "key": "Accept",
                "value": "application/pdf"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "method": "POST",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "einvoices",
                "render"
              ],
              "query": [],
              "raw": "{{base_url}}/v1/einvoices/render"
            }
          },
          "response": [
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"type\": \"https://ironfang.uk/problems/malformed_request\",\n  \"title\": \"Malformed request\",\n  \"status\": 400,\n  \"detail\": \"One or more options are invalid.\",\n  \"instance\": \"/financewolf/v1/einvoices/validate\",\n  \"code\": \"malformed_request\",\n  \"request_id\": \"req_01J9Q4TPD8XZM9P6Q2R8TVYB5G\",\n  \"errors\": [\n    {\n      \"field\": \"options.document_type\",\n      \"code\": \"invalid_value\",\n      \"message\": \"document_type must be invoice, credit_note or auto.\"\n    },\n    {\n      \"field\": \"options.deadline\",\n      \"code\": \"unknown_field\",\n      \"message\": \"deadline is not an option.\"\n    }\n  ]\n}",
              "code": 400,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/problem+json"
                }
              ],
              "name": "400 Bad option values",
              "originalRequest": {
                "body": {
                  "mode": "raw",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  },
                  "raw": "{\"operation_id\":\"{{operation_id}}\"}"
                },
                "description": "Requires financewolf:einvoices:read and a tenant-owned, unexpired successful\ngeneration operation. Supply only operation_id in a JSON body of at most\n1024 bytes. Validation-only and anonymous results cannot be rendered.\nUnknown or foreign IDs return 404; expired/deleted results return 410.\nQuery parameters, Idempotency-Key, customer HTML, URLs and render options\nare not accepted. The source XML and PHIVE verdict are checked before\nrendering; no new validation, operation, usage charge or PDF retention occurs.\nThe versioned template preserves XML values, labels the PDF as a readable\nrepresentation, and includes the source SHA-256 and original ruleset.\nA PDF is not a new validation verdict or a compliance certificate.\nAt most two render requests prepare source data per API process; excess\nrequests return 429 with Retry-After. Rendering has a 30-second deadline\nand a 32 MiB output limit; failure\nleaves the retained result and its original charge unchanged. PDF bytes\nmay vary across Chromium builds and request times. Responses are no-store.\n",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/pdf"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "method": "POST",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "render"
                  ],
                  "query": [],
                  "raw": "{{base_url}}/v1/einvoices/render"
                }
              },
              "status": "Bad Request"
            }
          ]
        }
      ],
      "name": "Rendering"
    },
    {
      "description": "Authenticated durable validation and generation jobs and ordered batches.",
      "item": [
        {
          "name": "List batches",
          "request": {
            "description": "Tenant-scoped metadata, newest first by creation time and ID. Equal timestamps retain stable pagination. Newer insertions do not appear on subsequent pages; refresh the first page to see them. No input payloads or child job bodies in batch summaries. A final page has an empty next_cursor. Responses are no-store. The JWT portal twin has the same response and pagination contract.",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "einvoices",
                "batches"
              ],
              "query": [
                {
                  "disabled": true,
                  "key": "limit",
                  "value": "{{limit}}"
                },
                {
                  "disabled": true,
                  "key": "cursor",
                  "value": "{{cursor}}"
                }
              ],
              "raw": "{{base_url}}/v1/einvoices/batches"
            }
          },
          "response": [
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"type\": \"https://ironfang.uk/problems/malformed_request\",\n  \"title\": \"Malformed request\",\n  \"status\": 400,\n  \"detail\": \"One or more options are invalid.\",\n  \"instance\": \"/financewolf/v1/einvoices/validate\",\n  \"code\": \"malformed_request\",\n  \"request_id\": \"req_01J9Q4TPD8XZM9P6Q2R8TVYB5G\",\n  \"errors\": [\n    {\n      \"field\": \"options.document_type\",\n      \"code\": \"invalid_value\",\n      \"message\": \"document_type must be invoice, credit_note or auto.\"\n    },\n    {\n      \"field\": \"options.deadline\",\n      \"code\": \"unknown_field\",\n      \"message\": \"deadline is not an option.\"\n    }\n  ]\n}",
              "code": 400,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/problem+json"
                }
              ],
              "name": "400 Bad option values",
              "originalRequest": {
                "description": "Tenant-scoped metadata, newest first by creation time and ID. Equal timestamps retain stable pagination. Newer insertions do not appear on subsequent pages; refresh the first page to see them. No input payloads or child job bodies in batch summaries. A final page has an empty next_cursor. Responses are no-store. The JWT portal twin has the same response and pagination contract.",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "method": "GET",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "batches"
                  ],
                  "query": [
                    {
                      "disabled": true,
                      "key": "limit",
                      "value": "{{limit}}"
                    },
                    {
                      "disabled": true,
                      "key": "cursor",
                      "value": "{{cursor}}"
                    }
                  ],
                  "raw": "{{base_url}}/v1/einvoices/batches"
                }
              },
              "status": "Bad Request"
            }
          ]
        },
        {
          "name": "Create an ordered batch",
          "request": {
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\"jobs\":[{\"operation\":\"validate\",\"document_base64\":\"{{document_base64}}\",\"options\":{\"ruleset\":\"{{ruleset}}\",\"profile\":\"{{profile}}\",\"document_type\":\"{{document_type}}\"}}]}"
            },
            "description": "Accept work durably using financewolf:einvoices:write. Jobs pin the selected\nruleset and engine at acceptance and use the same PHIVE/generator path as synchronous\nrequests. A batch has 1-100 ordered jobs; admission is atomic. Each input is at most\n5 MiB; JSON envelopes are limited to 8 MiB for a job and 32 MiB for a batch. The tenant\nbacklog is limited to 100 unfinished jobs and 25 MiB of original inputs across keys.\nAt most two jobs run per tenant across replicas. At most two admission envelopes are buffered per API process; excess returns 429. Encrypted inputs expire after 24 hours\nand are erased on completion or cancellation. Lease recovery and transient dependency\nfailures allow up to three attempts, with 5/10-second retry delays; attempts never add\nextra usage charges. Caller disconnection does not cancel accepted work. Use an\nIdempotency-Key (8-128 letters, digits, underscore or hyphen) when retrying an uncertain\nadmission. An identical request returns 200 current metadata; a different request under\nthe same key returns 409. Do not put invoice values in keys. Admission requires a\nconfigured queue encryption key; polling, cancellation and replays remain available\nwhen new admission is disabled. No query parameters or remote input URLs are accepted.\nAll responses are no-store.",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "disabled": true,
                "key": "Idempotency-Key",
                "value": "{{Idempotency-Key}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "method": "POST",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "einvoices",
                "batches"
              ],
              "query": [],
              "raw": "{{base_url}}/v1/einvoices/batches"
            }
          },
          "response": [
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"type\": \"https://ironfang.uk/problems/malformed_request\",\n  \"title\": \"Malformed request\",\n  \"status\": 400,\n  \"detail\": \"One or more options are invalid.\",\n  \"instance\": \"/financewolf/v1/einvoices/validate\",\n  \"code\": \"malformed_request\",\n  \"request_id\": \"req_01J9Q4TPD8XZM9P6Q2R8TVYB5G\",\n  \"errors\": [\n    {\n      \"field\": \"options.document_type\",\n      \"code\": \"invalid_value\",\n      \"message\": \"document_type must be invoice, credit_note or auto.\"\n    },\n    {\n      \"field\": \"options.deadline\",\n      \"code\": \"unknown_field\",\n      \"message\": \"deadline is not an option.\"\n    }\n  ]\n}",
              "code": 400,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/problem+json"
                }
              ],
              "name": "400 Bad option values",
              "originalRequest": {
                "body": {
                  "mode": "raw",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  },
                  "raw": "{\"jobs\":[{\"operation\":\"validate\",\"document_base64\":\"{{document_base64}}\",\"options\":{\"ruleset\":\"{{ruleset}}\",\"profile\":\"{{profile}}\",\"document_type\":\"{{document_type}}\"}}]}"
                },
                "description": "Accept work durably using financewolf:einvoices:write. Jobs pin the selected\nruleset and engine at acceptance and use the same PHIVE/generator path as synchronous\nrequests. A batch has 1-100 ordered jobs; admission is atomic. Each input is at most\n5 MiB; JSON envelopes are limited to 8 MiB for a job and 32 MiB for a batch. The tenant\nbacklog is limited to 100 unfinished jobs and 25 MiB of original inputs across keys.\nAt most two jobs run per tenant across replicas. At most two admission envelopes are buffered per API process; excess returns 429. Encrypted inputs expire after 24 hours\nand are erased on completion or cancellation. Lease recovery and transient dependency\nfailures allow up to three attempts, with 5/10-second retry delays; attempts never add\nextra usage charges. Caller disconnection does not cancel accepted work. Use an\nIdempotency-Key (8-128 letters, digits, underscore or hyphen) when retrying an uncertain\nadmission. An identical request returns 200 current metadata; a different request under\nthe same key returns 409. Do not put invoice values in keys. Admission requires a\nconfigured queue encryption key; polling, cancellation and replays remain available\nwhen new admission is disabled. No query parameters or remote input URLs are accepted.\nAll responses are no-store.",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "disabled": true,
                    "key": "Idempotency-Key",
                    "value": "{{Idempotency-Key}}"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "method": "POST",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "batches"
                  ],
                  "query": [],
                  "raw": "{{base_url}}/v1/einvoices/batches"
                }
              },
              "status": "Bad Request"
            }
          ]
        },
        {
          "name": "Get an ordered batch",
          "request": {
            "description": "Read tenant-owned job metadata with financewolf:einvoices:read. Unknown or foreign IDs\nreturn 404. Queued/running usage_charged=false is the current ledger state, not a promise\nthat completed work is free. A completed validation may be valid or invalid; service\nfailures are failed/indeterminate and uncharged. Use operation_id at /results/{id} for\nthe exact saved result (30-day retention). Batch children remain ordered by position;\ncompleted children are readable before the whole batch finishes. Mixed terminal\nstatuses produce partial. Metadata contains no input, credentials or lease token.\nNo query parameters are accepted; responses are no-store.",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "einvoices",
                "batches",
                "{{id}}"
              ],
              "query": [],
              "raw": "{{base_url}}/v1/einvoices/batches/{{id}}"
            }
          },
          "response": [
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"type\": \"https://ironfang.uk/problems/malformed_request\",\n  \"title\": \"Malformed request\",\n  \"status\": 400,\n  \"detail\": \"One or more options are invalid.\",\n  \"instance\": \"/financewolf/v1/einvoices/validate\",\n  \"code\": \"malformed_request\",\n  \"request_id\": \"req_01J9Q4TPD8XZM9P6Q2R8TVYB5G\",\n  \"errors\": [\n    {\n      \"field\": \"options.document_type\",\n      \"code\": \"invalid_value\",\n      \"message\": \"document_type must be invoice, credit_note or auto.\"\n    },\n    {\n      \"field\": \"options.deadline\",\n      \"code\": \"unknown_field\",\n      \"message\": \"deadline is not an option.\"\n    }\n  ]\n}",
              "code": 400,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/problem+json"
                }
              ],
              "name": "400 Bad option values",
              "originalRequest": {
                "description": "Read tenant-owned job metadata with financewolf:einvoices:read. Unknown or foreign IDs\nreturn 404. Queued/running usage_charged=false is the current ledger state, not a promise\nthat completed work is free. A completed validation may be valid or invalid; service\nfailures are failed/indeterminate and uncharged. Use operation_id at /results/{id} for\nthe exact saved result (30-day retention). Batch children remain ordered by position;\ncompleted children are readable before the whole batch finishes. Mixed terminal\nstatuses produce partial. Metadata contains no input, credentials or lease token.\nNo query parameters are accepted; responses are no-store.",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "method": "GET",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "batches",
                    "{{id}}"
                  ],
                  "query": [],
                  "raw": "{{base_url}}/v1/einvoices/batches/{{id}}"
                }
              },
              "status": "Bad Request"
            }
          ]
        },
        {
          "name": "Cancel an ordered batch",
          "request": {
            "description": "Cancel unfinished tenant-owned work with financewolf:einvoices:write. Send an empty body\nand no query parameters. Cancellation and completion are serialized: a completed job\nkeeps its result and usage; cancellation that wins prevents any later result or charge\nand erases the queued input. Repeated cancellation is idempotent. Batch cancellation\npreserves terminal children and cancels only queued/running children, atomically.\nUnknown or foreign IDs return 404. Responses are no-store.",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "method": "POST",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "einvoices",
                "batches",
                "{{id}}",
                "cancel"
              ],
              "query": [],
              "raw": "{{base_url}}/v1/einvoices/batches/{{id}}/cancel"
            }
          },
          "response": [
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"type\": \"https://ironfang.uk/problems/malformed_request\",\n  \"title\": \"Malformed request\",\n  \"status\": 400,\n  \"detail\": \"One or more options are invalid.\",\n  \"instance\": \"/financewolf/v1/einvoices/validate\",\n  \"code\": \"malformed_request\",\n  \"request_id\": \"req_01J9Q4TPD8XZM9P6Q2R8TVYB5G\",\n  \"errors\": [\n    {\n      \"field\": \"options.document_type\",\n      \"code\": \"invalid_value\",\n      \"message\": \"document_type must be invoice, credit_note or auto.\"\n    },\n    {\n      \"field\": \"options.deadline\",\n      \"code\": \"unknown_field\",\n      \"message\": \"deadline is not an option.\"\n    }\n  ]\n}",
              "code": 400,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/problem+json"
                }
              ],
              "name": "400 Bad option values",
              "originalRequest": {
                "description": "Cancel unfinished tenant-owned work with financewolf:einvoices:write. Send an empty body\nand no query parameters. Cancellation and completion are serialized: a completed job\nkeeps its result and usage; cancellation that wins prevents any later result or charge\nand erases the queued input. Repeated cancellation is idempotent. Batch cancellation\npreserves terminal children and cancels only queued/running children, atomically.\nUnknown or foreign IDs return 404. Responses are no-store.",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "method": "POST",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "batches",
                    "{{id}}",
                    "cancel"
                  ],
                  "query": [],
                  "raw": "{{base_url}}/v1/einvoices/batches/{{id}}/cancel"
                }
              },
              "status": "Bad Request"
            }
          ]
        },
        {
          "name": "List jobs",
          "request": {
            "description": "Tenant-scoped metadata, newest first by creation time and ID. Equal timestamps retain stable pagination. Newer insertions do not appear on subsequent pages; refresh the first page to see them. No input payloads or child job bodies in batch summaries. A final page has an empty next_cursor. Responses are no-store. The JWT portal twin has the same response and pagination contract.",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "einvoices",
                "jobs"
              ],
              "query": [
                {
                  "disabled": true,
                  "key": "limit",
                  "value": "{{limit}}"
                },
                {
                  "disabled": true,
                  "key": "cursor",
                  "value": "{{cursor}}"
                },
                {
                  "disabled": true,
                  "key": "status",
                  "value": "{{status}}"
                }
              ],
              "raw": "{{base_url}}/v1/einvoices/jobs"
            }
          },
          "response": [
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"type\": \"https://ironfang.uk/problems/malformed_request\",\n  \"title\": \"Malformed request\",\n  \"status\": 400,\n  \"detail\": \"One or more options are invalid.\",\n  \"instance\": \"/financewolf/v1/einvoices/validate\",\n  \"code\": \"malformed_request\",\n  \"request_id\": \"req_01J9Q4TPD8XZM9P6Q2R8TVYB5G\",\n  \"errors\": [\n    {\n      \"field\": \"options.document_type\",\n      \"code\": \"invalid_value\",\n      \"message\": \"document_type must be invoice, credit_note or auto.\"\n    },\n    {\n      \"field\": \"options.deadline\",\n      \"code\": \"unknown_field\",\n      \"message\": \"deadline is not an option.\"\n    }\n  ]\n}",
              "code": 400,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/problem+json"
                }
              ],
              "name": "400 Bad option values",
              "originalRequest": {
                "description": "Tenant-scoped metadata, newest first by creation time and ID. Equal timestamps retain stable pagination. Newer insertions do not appear on subsequent pages; refresh the first page to see them. No input payloads or child job bodies in batch summaries. A final page has an empty next_cursor. Responses are no-store. The JWT portal twin has the same response and pagination contract.",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "method": "GET",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "jobs"
                  ],
                  "query": [
                    {
                      "disabled": true,
                      "key": "limit",
                      "value": "{{limit}}"
                    },
                    {
                      "disabled": true,
                      "key": "cursor",
                      "value": "{{cursor}}"
                    },
                    {
                      "disabled": true,
                      "key": "status",
                      "value": "{{status}}"
                    }
                  ],
                  "raw": "{{base_url}}/v1/einvoices/jobs"
                }
              },
              "status": "Bad Request"
            }
          ]
        },
        {
          "name": "Create an async job",
          "request": {
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\"operation\":\"validate\",\"document_base64\":\"{{document_base64}}\",\"options\":{\"ruleset\":\"{{ruleset}}\",\"profile\":\"{{profile}}\",\"document_type\":\"{{document_type}}\"}}"
            },
            "description": "Accept work durably using financewolf:einvoices:write. Jobs pin the selected\nruleset and engine at acceptance and use the same PHIVE/generator path as synchronous\nrequests. A batch has 1-100 ordered jobs; admission is atomic. Each input is at most\n5 MiB; JSON envelopes are limited to 8 MiB for a job and 32 MiB for a batch. The tenant\nbacklog is limited to 100 unfinished jobs and 25 MiB of original inputs across keys.\nAt most two jobs run per tenant across replicas. At most two admission envelopes are buffered per API process; excess returns 429. Encrypted inputs expire after 24 hours\nand are erased on completion or cancellation. Lease recovery and transient dependency\nfailures allow up to three attempts, with 5/10-second retry delays; attempts never add\nextra usage charges. Caller disconnection does not cancel accepted work. Use an\nIdempotency-Key (8-128 letters, digits, underscore or hyphen) when retrying an uncertain\nadmission. An identical request returns 200 current metadata; a different request under\nthe same key returns 409. Do not put invoice values in keys. Admission requires a\nconfigured queue encryption key; polling, cancellation and replays remain available\nwhen new admission is disabled. No query parameters or remote input URLs are accepted.\nAll responses are no-store.",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "disabled": true,
                "key": "Idempotency-Key",
                "value": "{{Idempotency-Key}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "method": "POST",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "einvoices",
                "jobs"
              ],
              "query": [],
              "raw": "{{base_url}}/v1/einvoices/jobs"
            }
          },
          "response": [
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"type\": \"https://ironfang.uk/problems/malformed_request\",\n  \"title\": \"Malformed request\",\n  \"status\": 400,\n  \"detail\": \"One or more options are invalid.\",\n  \"instance\": \"/financewolf/v1/einvoices/validate\",\n  \"code\": \"malformed_request\",\n  \"request_id\": \"req_01J9Q4TPD8XZM9P6Q2R8TVYB5G\",\n  \"errors\": [\n    {\n      \"field\": \"options.document_type\",\n      \"code\": \"invalid_value\",\n      \"message\": \"document_type must be invoice, credit_note or auto.\"\n    },\n    {\n      \"field\": \"options.deadline\",\n      \"code\": \"unknown_field\",\n      \"message\": \"deadline is not an option.\"\n    }\n  ]\n}",
              "code": 400,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/problem+json"
                }
              ],
              "name": "400 Bad option values",
              "originalRequest": {
                "body": {
                  "mode": "raw",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  },
                  "raw": "{\"operation\":\"validate\",\"document_base64\":\"{{document_base64}}\",\"options\":{\"ruleset\":\"{{ruleset}}\",\"profile\":\"{{profile}}\",\"document_type\":\"{{document_type}}\"}}"
                },
                "description": "Accept work durably using financewolf:einvoices:write. Jobs pin the selected\nruleset and engine at acceptance and use the same PHIVE/generator path as synchronous\nrequests. A batch has 1-100 ordered jobs; admission is atomic. Each input is at most\n5 MiB; JSON envelopes are limited to 8 MiB for a job and 32 MiB for a batch. The tenant\nbacklog is limited to 100 unfinished jobs and 25 MiB of original inputs across keys.\nAt most two jobs run per tenant across replicas. At most two admission envelopes are buffered per API process; excess returns 429. Encrypted inputs expire after 24 hours\nand are erased on completion or cancellation. Lease recovery and transient dependency\nfailures allow up to three attempts, with 5/10-second retry delays; attempts never add\nextra usage charges. Caller disconnection does not cancel accepted work. Use an\nIdempotency-Key (8-128 letters, digits, underscore or hyphen) when retrying an uncertain\nadmission. An identical request returns 200 current metadata; a different request under\nthe same key returns 409. Do not put invoice values in keys. Admission requires a\nconfigured queue encryption key; polling, cancellation and replays remain available\nwhen new admission is disabled. No query parameters or remote input URLs are accepted.\nAll responses are no-store.",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "disabled": true,
                    "key": "Idempotency-Key",
                    "value": "{{Idempotency-Key}}"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "method": "POST",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "jobs"
                  ],
                  "query": [],
                  "raw": "{{base_url}}/v1/einvoices/jobs"
                }
              },
              "status": "Bad Request"
            }
          ]
        },
        {
          "name": "Get an async job",
          "request": {
            "description": "Read tenant-owned job metadata with financewolf:einvoices:read. Unknown or foreign IDs\nreturn 404. Queued/running usage_charged=false is the current ledger state, not a promise\nthat completed work is free. A completed validation may be valid or invalid; service\nfailures are failed/indeterminate and uncharged. Use operation_id at /results/{id} for\nthe exact saved result (30-day retention). Batch children remain ordered by position;\ncompleted children are readable before the whole batch finishes. Mixed terminal\nstatuses produce partial. Metadata contains no input, credentials or lease token.\nNo query parameters are accepted; responses are no-store.",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "einvoices",
                "jobs",
                "{{id}}"
              ],
              "query": [],
              "raw": "{{base_url}}/v1/einvoices/jobs/{{id}}"
            }
          },
          "response": [
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"type\": \"https://ironfang.uk/problems/malformed_request\",\n  \"title\": \"Malformed request\",\n  \"status\": 400,\n  \"detail\": \"One or more options are invalid.\",\n  \"instance\": \"/financewolf/v1/einvoices/validate\",\n  \"code\": \"malformed_request\",\n  \"request_id\": \"req_01J9Q4TPD8XZM9P6Q2R8TVYB5G\",\n  \"errors\": [\n    {\n      \"field\": \"options.document_type\",\n      \"code\": \"invalid_value\",\n      \"message\": \"document_type must be invoice, credit_note or auto.\"\n    },\n    {\n      \"field\": \"options.deadline\",\n      \"code\": \"unknown_field\",\n      \"message\": \"deadline is not an option.\"\n    }\n  ]\n}",
              "code": 400,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/problem+json"
                }
              ],
              "name": "400 Bad option values",
              "originalRequest": {
                "description": "Read tenant-owned job metadata with financewolf:einvoices:read. Unknown or foreign IDs\nreturn 404. Queued/running usage_charged=false is the current ledger state, not a promise\nthat completed work is free. A completed validation may be valid or invalid; service\nfailures are failed/indeterminate and uncharged. Use operation_id at /results/{id} for\nthe exact saved result (30-day retention). Batch children remain ordered by position;\ncompleted children are readable before the whole batch finishes. Mixed terminal\nstatuses produce partial. Metadata contains no input, credentials or lease token.\nNo query parameters are accepted; responses are no-store.",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "method": "GET",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "jobs",
                    "{{id}}"
                  ],
                  "query": [],
                  "raw": "{{base_url}}/v1/einvoices/jobs/{{id}}"
                }
              },
              "status": "Bad Request"
            }
          ]
        },
        {
          "name": "Cancel an async job",
          "request": {
            "description": "Cancel unfinished tenant-owned work with financewolf:einvoices:write. Send an empty body\nand no query parameters. Cancellation and completion are serialized: a completed job\nkeeps its result and usage; cancellation that wins prevents any later result or charge\nand erases the queued input. Repeated cancellation is idempotent. Batch cancellation\npreserves terminal children and cancels only queued/running children, atomically.\nUnknown or foreign IDs return 404. Responses are no-store.",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "method": "POST",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "einvoices",
                "jobs",
                "{{id}}",
                "cancel"
              ],
              "query": [],
              "raw": "{{base_url}}/v1/einvoices/jobs/{{id}}/cancel"
            }
          },
          "response": [
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"type\": \"https://ironfang.uk/problems/malformed_request\",\n  \"title\": \"Malformed request\",\n  \"status\": 400,\n  \"detail\": \"One or more options are invalid.\",\n  \"instance\": \"/financewolf/v1/einvoices/validate\",\n  \"code\": \"malformed_request\",\n  \"request_id\": \"req_01J9Q4TPD8XZM9P6Q2R8TVYB5G\",\n  \"errors\": [\n    {\n      \"field\": \"options.document_type\",\n      \"code\": \"invalid_value\",\n      \"message\": \"document_type must be invoice, credit_note or auto.\"\n    },\n    {\n      \"field\": \"options.deadline\",\n      \"code\": \"unknown_field\",\n      \"message\": \"deadline is not an option.\"\n    }\n  ]\n}",
              "code": 400,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/problem+json"
                }
              ],
              "name": "400 Bad option values",
              "originalRequest": {
                "description": "Cancel unfinished tenant-owned work with financewolf:einvoices:write. Send an empty body\nand no query parameters. Cancellation and completion are serialized: a completed job\nkeeps its result and usage; cancellation that wins prevents any later result or charge\nand erases the queued input. Repeated cancellation is idempotent. Batch cancellation\npreserves terminal children and cancels only queued/running children, atomically.\nUnknown or foreign IDs return 404. Responses are no-store.",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "method": "POST",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "jobs",
                    "{{id}}",
                    "cancel"
                  ],
                  "query": [],
                  "raw": "{{base_url}}/v1/einvoices/jobs/{{id}}/cancel"
                }
              },
              "status": "Bad Request"
            }
          ]
        }
      ],
      "name": "Jobs"
    },
    {
      "description": "Destinations subscribe to future terminal validation/generation operations in their organisation. Delivery is at least once, independently retried, and never changes validation, usage or Peppol network state. Event IDs and payload bytes are immutable; deduplicate IDs and tolerate out-of-order arrival. Events/history expire after 30 days. Signed webhooks use Financewolf-Event-Id, Financewolf-Timestamp and Financewolf-Signature (v1=HMAC-SHA256 of timestamp + dot + exact body, with the hex-decoded secret). Verify the signature with a constant-time comparison, reject timestamps outside a five-minute tolerance, and persist event IDs before acknowledging. S3 writes retained result.json and generated document.xml under prefix/financewolf/{event_id}/, then manifest.json last. Validation source XML, PDFs and signed report ZIPs are not automatically exported. S3-compatible endpoints must honour conditional PutObject (If-None-Match: *); existing objects are checked by SHA-256 metadata and size. Customer bucket copies follow customer retention; deleting a Financewolf result cannot revoke an upload already in flight. HTTPS public destinations only; query parameters, URL credentials, fragments and redirects are refused. Maximum 10 non-retired destinations; 8 initial attempts and at most 32 including manual retries. Disable before rotating secrets if a coordinated receiver cutover is needed; in-flight attempts may use the previous key.",
      "item": [
        {
          "name": "List delivery history with keyset pagination",
          "request": {
            "description": "List delivery history with keyset pagination. Requires the listed API-key scope. See the deliveries tag for event, signature, retry, storage and retention semantics. Invoice validation and usage are unchanged by delivery.",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "einvoices",
                "deliveries"
              ],
              "query": [
                {
                  "disabled": true,
                  "key": "cursor",
                  "value": "{{cursor}}"
                },
                {
                  "disabled": true,
                  "key": "limit",
                  "value": "{{limit}}"
                }
              ],
              "raw": "{{base_url}}/v1/einvoices/deliveries"
            }
          },
          "response": [
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"type\": \"https://ironfang.uk/problems/malformed_request\",\n  \"title\": \"Malformed request\",\n  \"status\": 400,\n  \"detail\": \"One or more options are invalid.\",\n  \"instance\": \"/financewolf/v1/einvoices/validate\",\n  \"code\": \"malformed_request\",\n  \"request_id\": \"req_01J9Q4TPD8XZM9P6Q2R8TVYB5G\",\n  \"errors\": [\n    {\n      \"field\": \"options.document_type\",\n      \"code\": \"invalid_value\",\n      \"message\": \"document_type must be invoice, credit_note or auto.\"\n    },\n    {\n      \"field\": \"options.deadline\",\n      \"code\": \"unknown_field\",\n      \"message\": \"deadline is not an option.\"\n    }\n  ]\n}",
              "code": 400,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/problem+json"
                }
              ],
              "name": "400 Bad option values",
              "originalRequest": {
                "description": "List delivery history with keyset pagination. Requires the listed API-key scope. See the deliveries tag for event, signature, retry, storage and retention semantics. Invoice validation and usage are unchanged by delivery.",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "method": "GET",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "deliveries"
                  ],
                  "query": [
                    {
                      "disabled": true,
                      "key": "cursor",
                      "value": "{{cursor}}"
                    },
                    {
                      "disabled": true,
                      "key": "limit",
                      "value": "{{limit}}"
                    }
                  ],
                  "raw": "{{base_url}}/v1/einvoices/deliveries"
                }
              },
              "status": "Bad Request"
            }
          ]
        },
        {
          "name": "Read a delivery and its immutable attempt history",
          "request": {
            "description": "Read a delivery and its immutable attempt history. Requires the listed API-key scope. See the deliveries tag for event, signature, retry, storage and retention semantics. Invoice validation and usage are unchanged by delivery.",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "einvoices",
                "deliveries",
                "{{id}}"
              ],
              "query": [],
              "raw": "{{base_url}}/v1/einvoices/deliveries/{{id}}"
            }
          },
          "response": [
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"type\": \"https://ironfang.uk/problems/malformed_request\",\n  \"title\": \"Malformed request\",\n  \"status\": 400,\n  \"detail\": \"One or more options are invalid.\",\n  \"instance\": \"/financewolf/v1/einvoices/validate\",\n  \"code\": \"malformed_request\",\n  \"request_id\": \"req_01J9Q4TPD8XZM9P6Q2R8TVYB5G\",\n  \"errors\": [\n    {\n      \"field\": \"options.document_type\",\n      \"code\": \"invalid_value\",\n      \"message\": \"document_type must be invoice, credit_note or auto.\"\n    },\n    {\n      \"field\": \"options.deadline\",\n      \"code\": \"unknown_field\",\n      \"message\": \"deadline is not an option.\"\n    }\n  ]\n}",
              "code": 400,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/problem+json"
                }
              ],
              "name": "400 Bad option values",
              "originalRequest": {
                "description": "Read a delivery and its immutable attempt history. Requires the listed API-key scope. See the deliveries tag for event, signature, retry, storage and retention semantics. Invoice validation and usage are unchanged by delivery.",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "method": "GET",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "deliveries",
                    "{{id}}"
                  ],
                  "query": [],
                  "raw": "{{base_url}}/v1/einvoices/deliveries/{{id}}"
                }
              },
              "status": "Bad Request"
            }
          ]
        },
        {
          "name": "Retry failed delivery without rerunning validation",
          "request": {
            "description": "Retry failed delivery without rerunning validation. Requires the listed API-key scope. See the deliveries tag for event, signature, retry, storage and retention semantics. Invoice validation and usage are unchanged by delivery.",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "method": "POST",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "einvoices",
                "deliveries",
                "{{id}}",
                "retry"
              ],
              "query": [],
              "raw": "{{base_url}}/v1/einvoices/deliveries/{{id}}/retry"
            }
          },
          "response": [
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"type\": \"https://ironfang.uk/problems/malformed_request\",\n  \"title\": \"Malformed request\",\n  \"status\": 400,\n  \"detail\": \"One or more options are invalid.\",\n  \"instance\": \"/financewolf/v1/einvoices/validate\",\n  \"code\": \"malformed_request\",\n  \"request_id\": \"req_01J9Q4TPD8XZM9P6Q2R8TVYB5G\",\n  \"errors\": [\n    {\n      \"field\": \"options.document_type\",\n      \"code\": \"invalid_value\",\n      \"message\": \"document_type must be invoice, credit_note or auto.\"\n    },\n    {\n      \"field\": \"options.deadline\",\n      \"code\": \"unknown_field\",\n      \"message\": \"deadline is not an option.\"\n    }\n  ]\n}",
              "code": 400,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/problem+json"
                }
              ],
              "name": "400 Bad option values",
              "originalRequest": {
                "description": "Retry failed delivery without rerunning validation. Requires the listed API-key scope. See the deliveries tag for event, signature, retry, storage and retention semantics. Invoice validation and usage are unchanged by delivery.",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "method": "POST",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "deliveries",
                    "{{id}}",
                    "retry"
                  ],
                  "query": [],
                  "raw": "{{base_url}}/v1/einvoices/deliveries/{{id}}/retry"
                }
              },
              "status": "Bad Request"
            }
          ]
        },
        {
          "name": "List active destination configurations",
          "request": {
            "description": "List active destination configurations. Requires the listed API-key scope. See the deliveries tag for event, signature, retry, storage and retention semantics. Invoice validation and usage are unchanged by delivery.",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "einvoices",
                "destinations"
              ],
              "query": [],
              "raw": "{{base_url}}/v1/einvoices/destinations"
            }
          },
          "response": [
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"type\": \"https://ironfang.uk/problems/malformed_request\",\n  \"title\": \"Malformed request\",\n  \"status\": 400,\n  \"detail\": \"One or more options are invalid.\",\n  \"instance\": \"/financewolf/v1/einvoices/validate\",\n  \"code\": \"malformed_request\",\n  \"request_id\": \"req_01J9Q4TPD8XZM9P6Q2R8TVYB5G\",\n  \"errors\": [\n    {\n      \"field\": \"options.document_type\",\n      \"code\": \"invalid_value\",\n      \"message\": \"document_type must be invoice, credit_note or auto.\"\n    },\n    {\n      \"field\": \"options.deadline\",\n      \"code\": \"unknown_field\",\n      \"message\": \"deadline is not an option.\"\n    }\n  ]\n}",
              "code": 400,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/problem+json"
                }
              ],
              "name": "400 Bad option values",
              "originalRequest": {
                "description": "List active destination configurations. Requires the listed API-key scope. See the deliveries tag for event, signature, retry, storage and retention semantics. Invoice validation and usage are unchanged by delivery.",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "method": "GET",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "destinations"
                  ],
                  "query": [],
                  "raw": "{{base_url}}/v1/einvoices/destinations"
                }
              },
              "status": "Bad Request"
            }
          ]
        },
        {
          "name": "Create a webhook or S3 destination",
          "request": {
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\"type\":\"webhook\",\"name\":\"Financewolf events\",\"config\":{\"url\":\"{{webhook_url}}\"}}"
            },
            "description": "Create a webhook or S3 destination. Requires the listed API-key scope. See the deliveries tag for event, signature, retry, storage and retention semantics. Invoice validation and usage are unchanged by delivery.",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "method": "POST",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "einvoices",
                "destinations"
              ],
              "query": [],
              "raw": "{{base_url}}/v1/einvoices/destinations"
            }
          },
          "response": [
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"type\": \"https://ironfang.uk/problems/malformed_request\",\n  \"title\": \"Malformed request\",\n  \"status\": 400,\n  \"detail\": \"One or more options are invalid.\",\n  \"instance\": \"/financewolf/v1/einvoices/validate\",\n  \"code\": \"malformed_request\",\n  \"request_id\": \"req_01J9Q4TPD8XZM9P6Q2R8TVYB5G\",\n  \"errors\": [\n    {\n      \"field\": \"options.document_type\",\n      \"code\": \"invalid_value\",\n      \"message\": \"document_type must be invoice, credit_note or auto.\"\n    },\n    {\n      \"field\": \"options.deadline\",\n      \"code\": \"unknown_field\",\n      \"message\": \"deadline is not an option.\"\n    }\n  ]\n}",
              "code": 400,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/problem+json"
                }
              ],
              "name": "400 Bad option values",
              "originalRequest": {
                "body": {
                  "mode": "raw",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  },
                  "raw": "{\"type\":\"webhook\",\"name\":\"Financewolf events\",\"config\":{\"url\":\"{{webhook_url}}\"}}"
                },
                "description": "Create a webhook or S3 destination. Requires the listed API-key scope. See the deliveries tag for event, signature, retry, storage and retention semantics. Invoice validation and usage are unchanged by delivery.",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "method": "POST",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "destinations"
                  ],
                  "query": [],
                  "raw": "{{base_url}}/v1/einvoices/destinations"
                }
              },
              "status": "Bad Request"
            }
          ]
        },
        {
          "name": "Read a destination without its credentials",
          "request": {
            "description": "Read a destination without its credentials. Requires the listed API-key scope. See the deliveries tag for event, signature, retry, storage and retention semantics. Invoice validation and usage are unchanged by delivery.",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "einvoices",
                "destinations",
                "{{id}}"
              ],
              "query": [],
              "raw": "{{base_url}}/v1/einvoices/destinations/{{id}}"
            }
          },
          "response": [
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"type\": \"https://ironfang.uk/problems/malformed_request\",\n  \"title\": \"Malformed request\",\n  \"status\": 400,\n  \"detail\": \"One or more options are invalid.\",\n  \"instance\": \"/financewolf/v1/einvoices/validate\",\n  \"code\": \"malformed_request\",\n  \"request_id\": \"req_01J9Q4TPD8XZM9P6Q2R8TVYB5G\",\n  \"errors\": [\n    {\n      \"field\": \"options.document_type\",\n      \"code\": \"invalid_value\",\n      \"message\": \"document_type must be invoice, credit_note or auto.\"\n    },\n    {\n      \"field\": \"options.deadline\",\n      \"code\": \"unknown_field\",\n      \"message\": \"deadline is not an option.\"\n    }\n  ]\n}",
              "code": 400,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/problem+json"
                }
              ],
              "name": "400 Bad option values",
              "originalRequest": {
                "description": "Read a destination without its credentials. Requires the listed API-key scope. See the deliveries tag for event, signature, retry, storage and retention semantics. Invoice validation and usage are unchanged by delivery.",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "method": "GET",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "destinations",
                    "{{id}}"
                  ],
                  "query": [],
                  "raw": "{{base_url}}/v1/einvoices/destinations/{{id}}"
                }
              },
              "status": "Bad Request"
            }
          ]
        },
        {
          "name": "Retire a destination and erase its stored credentials",
          "request": {
            "description": "Retire a destination and erase its stored credentials. Requires the listed API-key scope. See the deliveries tag for event, signature, retry, storage and retention semantics. Invoice validation and usage are unchanged by delivery.",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "method": "DELETE",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "einvoices",
                "destinations",
                "{{id}}"
              ],
              "query": [],
              "raw": "{{base_url}}/v1/einvoices/destinations/{{id}}"
            }
          },
          "response": [
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"type\": \"https://ironfang.uk/problems/malformed_request\",\n  \"title\": \"Malformed request\",\n  \"status\": 400,\n  \"detail\": \"One or more options are invalid.\",\n  \"instance\": \"/financewolf/v1/einvoices/validate\",\n  \"code\": \"malformed_request\",\n  \"request_id\": \"req_01J9Q4TPD8XZM9P6Q2R8TVYB5G\",\n  \"errors\": [\n    {\n      \"field\": \"options.document_type\",\n      \"code\": \"invalid_value\",\n      \"message\": \"document_type must be invoice, credit_note or auto.\"\n    },\n    {\n      \"field\": \"options.deadline\",\n      \"code\": \"unknown_field\",\n      \"message\": \"deadline is not an option.\"\n    }\n  ]\n}",
              "code": 400,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/problem+json"
                }
              ],
              "name": "400 Bad option values",
              "originalRequest": {
                "description": "Retire a destination and erase its stored credentials. Requires the listed API-key scope. See the deliveries tag for event, signature, retry, storage and retention semantics. Invoice validation and usage are unchanged by delivery.",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "method": "DELETE",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "destinations",
                    "{{id}}"
                  ],
                  "query": [],
                  "raw": "{{base_url}}/v1/einvoices/destinations/{{id}}"
                }
              },
              "status": "Bad Request"
            }
          ]
        },
        {
          "name": "Enable, disable or rotate destination credentials",
          "request": {
            "body": {
              "mode": "raw",
              "options": {
                "raw": {
                  "language": "json"
                }
              },
              "raw": "{\"enabled\":true}"
            },
            "description": "Enable, disable or rotate destination credentials. Requires the listed API-key scope. See the deliveries tag for event, signature, retry, storage and retention semantics. Invoice validation and usage are unchanged by delivery.",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "method": "PATCH",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "einvoices",
                "destinations",
                "{{id}}"
              ],
              "query": [],
              "raw": "{{base_url}}/v1/einvoices/destinations/{{id}}"
            }
          },
          "response": [
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"type\": \"https://ironfang.uk/problems/malformed_request\",\n  \"title\": \"Malformed request\",\n  \"status\": 400,\n  \"detail\": \"One or more options are invalid.\",\n  \"instance\": \"/financewolf/v1/einvoices/validate\",\n  \"code\": \"malformed_request\",\n  \"request_id\": \"req_01J9Q4TPD8XZM9P6Q2R8TVYB5G\",\n  \"errors\": [\n    {\n      \"field\": \"options.document_type\",\n      \"code\": \"invalid_value\",\n      \"message\": \"document_type must be invoice, credit_note or auto.\"\n    },\n    {\n      \"field\": \"options.deadline\",\n      \"code\": \"unknown_field\",\n      \"message\": \"deadline is not an option.\"\n    }\n  ]\n}",
              "code": 400,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/problem+json"
                }
              ],
              "name": "400 Bad option values",
              "originalRequest": {
                "body": {
                  "mode": "raw",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  },
                  "raw": "{\"enabled\":true}"
                },
                "description": "Enable, disable or rotate destination credentials. Requires the listed API-key scope. See the deliveries tag for event, signature, retry, storage and retention semantics. Invoice validation and usage are unchanged by delivery.",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "method": "PATCH",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "destinations",
                    "{{id}}"
                  ],
                  "query": [],
                  "raw": "{{base_url}}/v1/einvoices/destinations/{{id}}"
                }
              },
              "status": "Bad Request"
            }
          ]
        }
      ],
      "name": "Deliveries"
    },
    {
      "description": "Financewolf document catalogue, entitlements and content-free usage reconciliation.",
      "item": [
        {
          "name": "Read Financewolf document entitlements",
          "request": {
            "description": "Requires financewolf:billing:manage. The catalogue is proposed while enabled=false. Quotas count charged and reserved operations in their admission period; Renderwolf credits are separate. Usage dates select admission periods, not completion timestamps.",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "einvoices",
                "billing"
              ],
              "query": [],
              "raw": "{{base_url}}/v1/einvoices/billing"
            }
          },
          "response": [
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"type\": \"https://ironfang.uk/problems/malformed_request\",\n  \"title\": \"Malformed request\",\n  \"status\": 400,\n  \"detail\": \"One or more options are invalid.\",\n  \"instance\": \"/financewolf/v1/einvoices/validate\",\n  \"code\": \"malformed_request\",\n  \"request_id\": \"req_01J9Q4TPD8XZM9P6Q2R8TVYB5G\",\n  \"errors\": [\n    {\n      \"field\": \"options.document_type\",\n      \"code\": \"invalid_value\",\n      \"message\": \"document_type must be invoice, credit_note or auto.\"\n    },\n    {\n      \"field\": \"options.deadline\",\n      \"code\": \"unknown_field\",\n      \"message\": \"deadline is not an option.\"\n    }\n  ]\n}",
              "code": 400,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/problem+json"
                }
              ],
              "name": "400 Bad option values",
              "originalRequest": {
                "description": "Requires financewolf:billing:manage. The catalogue is proposed while enabled=false. Quotas count charged and reserved operations in their admission period; Renderwolf credits are separate. Usage dates select admission periods, not completion timestamps.",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "method": "GET",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "billing"
                  ],
                  "query": [],
                  "raw": "{{base_url}}/v1/einvoices/billing"
                }
              },
              "status": "Bad Request"
            }
          ]
        },
        {
          "name": "Reconcile Financewolf document usage",
          "request": {
            "description": "Requires financewolf:billing:manage. The catalogue is proposed while enabled=false. Quotas count charged and reserved operations in their admission period; Renderwolf credits are separate. Usage dates select admission periods, not completion timestamps.",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "method": "GET",
            "url": {
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "einvoices",
                "usage"
              ],
              "query": [
                {
                  "disabled": false,
                  "key": "start",
                  "value": "{{start}}"
                },
                {
                  "disabled": false,
                  "key": "end",
                  "value": "{{end}}"
                }
              ],
              "raw": "{{base_url}}/v1/einvoices/usage?start={{start}}\u0026end={{end}}"
            }
          },
          "response": [
            {
              "_postman_previewlanguage": "json",
              "body": "{\n  \"type\": \"https://ironfang.uk/problems/malformed_request\",\n  \"title\": \"Malformed request\",\n  \"status\": 400,\n  \"detail\": \"One or more options are invalid.\",\n  \"instance\": \"/financewolf/v1/einvoices/validate\",\n  \"code\": \"malformed_request\",\n  \"request_id\": \"req_01J9Q4TPD8XZM9P6Q2R8TVYB5G\",\n  \"errors\": [\n    {\n      \"field\": \"options.document_type\",\n      \"code\": \"invalid_value\",\n      \"message\": \"document_type must be invoice, credit_note or auto.\"\n    },\n    {\n      \"field\": \"options.deadline\",\n      \"code\": \"unknown_field\",\n      \"message\": \"deadline is not an option.\"\n    }\n  ]\n}",
              "code": 400,
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/problem+json"
                }
              ],
              "name": "400 Bad option values",
              "originalRequest": {
                "description": "Requires financewolf:billing:manage. The catalogue is proposed while enabled=false. Quotas count charged and reserved operations in their admission period; Renderwolf credits are separate. Usage dates select admission periods, not completion timestamps.",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "method": "GET",
                "url": {
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "v1",
                    "einvoices",
                    "usage"
                  ],
                  "query": [
                    {
                      "disabled": false,
                      "key": "start",
                      "value": "{{start}}"
                    },
                    {
                      "disabled": false,
                      "key": "end",
                      "value": "{{end}}"
                    }
                  ],
                  "raw": "{{base_url}}/v1/einvoices/usage?start={{start}}\u0026end={{end}}"
                }
              },
              "status": "Bad Request"
            }
          ]
        }
      ],
      "name": "Billing"
    }
  ],
  "variable": [
    {
      "key": "Idempotency-Key",
      "type": "string",
      "value": ""
    },
    {
      "key": "base_url",
      "type": "string",
      "value": "https://api.ironfang.uk/financewolf"
    },
    {
      "key": "before",
      "type": "string",
      "value": ""
    },
    {
      "key": "cursor",
      "type": "string",
      "value": ""
    },
    {
      "key": "document_base64",
      "type": "string",
      "value": ""
    },
    {
      "key": "document_type",
      "type": "string",
      "value": "auto"
    },
    {
      "key": "end",
      "type": "string",
      "value": ""
    },
    {
      "key": "generation_input",
      "type": "string",
      "value": ""
    },
    {
      "key": "id",
      "type": "string",
      "value": ""
    },
    {
      "key": "limit",
      "type": "string",
      "value": ""
    },
    {
      "key": "operation_id",
      "type": "string",
      "value": ""
    },
    {
      "key": "profile",
      "type": "string",
      "value": "peppol-bis-billing-3"
    },
    {
      "key": "ruleset",
      "type": "string",
      "value": "latest"
    },
    {
      "key": "start",
      "type": "string",
      "value": ""
    },
    {
      "key": "state",
      "type": "string",
      "value": ""
    },
    {
      "key": "status",
      "type": "string",
      "value": ""
    },
    {
      "key": "webhook_url",
      "type": "string",
      "value": ""
    }
  ]
}
