{"components":{"headers":{"RetryAfter":{"description":"Seconds to wait before retrying. Mirrors `retry_after_seconds` in the body.","schema":{"minimum":1,"type":"integer"}},"XIronfangRequestID":{"description":"Ironfang's internal request id, minted at the edge for every request and carried in every Problem body as `request_id`. Quote it in support requests.","schema":{"$ref":"#/components/schemas/RequestID"}},"XRequestIDEcho":{"description":"Your own correlation id, echoed back when the request carried a well-formed `X-Request-ID`: visible ASCII only (bytes 0x21 to 0x7E, so no spaces or control characters), 1 to 128 bytes. Anything else is ignored, not echoed. Never used as Ironfang's identifier for the request.","required":false,"schema":{"maxLength":128,"minLength":1,"pattern":"^[\\x21-\\x7E]{1,128}$","type":"string"}}},"parameters":{"IdempotencyKey":{"description":"Optional on authenticated requests. The same key with the same request replays the original operation; the same key with a different request (different document bytes, ruleset, profile or document type) is `409 idempotency_conflict`. Scoped to the organisation and the validation endpoint. Anonymous requests never use it.","in":"header","name":"Idempotency-Key","required":false,"schema":{"maxLength":128,"minLength":8,"pattern":"^[A-Za-z0-9_-]+$","type":"string"}}},"responses":{"AdmissionGone":{"content":{"application/problem+json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Problem"},{"properties":{"code":{"enum":["ruleset_disabled","account_closed"]},"status":{"const":410}}}]}}},"description":"The release was withdrawn from every use, or the billing account is closed. Nothing was queued.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"DeliveryRateLimited":{"content":{"application/problem+json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Problem"},{"not":{"anyOf":[{"required":["outcome"]},{"required":["usage_charged"]},{"required":["operation_id"]}]},"properties":{"code":{"enum":["rate_limited","quota_exhausted"]},"status":{"const":429}}}]}}},"description":"Delivery management failed; the invoice outcome is unchanged.","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"},"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"DeliveryUnavailable":{"content":{"application/problem+json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Problem"},{"not":{"anyOf":[{"required":["outcome"]},{"required":["usage_charged"]},{"required":["operation_id"]}]},"properties":{"code":{"enum":["delivery_unavailable"]},"status":{"const":503}}}]}}},"description":"Delivery management failed; the invoice outcome is unchanged.","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"},"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"IdempotencyConflict":{"content":{"application/problem+json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Problem"},{"properties":{"code":{"enum":["idempotency_conflict"]},"status":{"const":409}}}]}}},"description":"The `Idempotency-Key` was already used by this organisation, on the validation endpoint, for a request with different content. The original operation is left untouched; send a new key for a new request.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"InsufficientScope":{"content":{"application/problem+json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Problem"},{"properties":{"code":{"enum":["insufficient_scope"]},"status":{"const":403}}}]}}},"description":"The key is valid but does not carry the scope this operation needs.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"MalformedRequest":{"content":{"application/problem+json":{"examples":{"field-errors":{"externalValue":"https://api.ironfang.com/finance/examples/v1-problem-field-errors.json","summary":"Bad option values"}},"schema":{"allOf":[{"$ref":"#/components/schemas/Problem"},{"properties":{"code":{"enum":["malformed_request"]},"status":{"const":400}}}]}}},"description":"The request itself is wrong - not the document being validated.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"NotFound":{"content":{"application/problem+json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Problem"},{"properties":{"code":{"enum":["not_found"]},"status":{"const":404}}}]}}},"description":"No such ruleset visible to this caller (unknown, an internal candidate, or the keyword `latest`).","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"PayloadTooLarge":{"content":{"application/problem+json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Problem"},{"properties":{"code":{"enum":["payload_too_large"]},"status":{"const":413}}}]}}},"description":"The body exceeds 5 MiB.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"PaymentRequired":{"content":{"application/problem+json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Problem"},{"properties":{"code":{"enum":["paid_usage_paused","payment_required","payment_action_required","account_restricted"]},"status":{"const":402}}}]}}},"description":"The free allowance is used up and paid usage cannot continue until the billing account is put right: paused, no working payment method, a payment awaiting confirmation, or restricted. No work or usage was accepted."},"ReadInternalError":{"content":{"application/problem+json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Problem"},{"not":{"anyOf":[{"required":["outcome"]},{"required":["usage_charged"]},{"required":["operation_id"]}]},"properties":{"code":{"enum":["internal_error"]},"status":{"const":500}}}]}}},"description":"Ironfang Finance failed while reading the registry. Not a validation verdict: the body carries no outcome or usage fields.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"ReadRateLimited":{"content":{"application/problem+json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Problem"},{"properties":{"code":{"enum":["rate_limited"]},"status":{"const":429}}}]}}},"description":"Per-address or per-key rate limit. Ruleset reads are not metered, so no quota problem is possible here.","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"},"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"ResultGone":{"content":{"application/problem+json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Problem"},{"properties":{"code":{"enum":["result_gone"]},"status":{"const":410}}}]}}},"description":"This validation result expired or was deleted. Operation and usage metadata remain.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"RulesetDisabled":{"content":{"application/problem+json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Problem"},{"properties":{"code":{"enum":["ruleset_disabled"]},"status":{"const":410}}}]}}},"description":"The ruleset id is known but the release has been withdrawn from every use, validation included.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"ScopeOrEligibility":{"content":{"application/problem+json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Problem"},{"properties":{"code":{"enum":["insufficient_scope","product_not_eligible"]},"status":{"const":403}}}]}}},"description":"The key lacks the scope this operation needs, or paid Ironfang Finance usage needs an approved business and the free allowance is used up. No work or usage was accepted.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"Unauthenticated":{"content":{"application/problem+json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Problem"},{"properties":{"code":{"enum":["unauthenticated"]},"status":{"const":401}}}]}}},"description":"A credential was presented and could not be verified. Anonymous access, where permitted, sends no credential at all.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"UnsupportedContentType":{"content":{"application/problem+json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Problem"},{"properties":{"code":{"enum":["unsupported_content_type"]},"status":{"const":415}}}]}}},"description":"The body is not XML (an archive, a compressed body, or another media type).","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"UnsupportedSelection":{"content":{"application/problem+json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Problem"},{"properties":{"code":{"enum":["unknown_ruleset","unsupported_profile","profile_undetected","unsupported_document_type"]},"status":{"const":422}}}]}}},"description":"The requested ruleset, profile or document type cannot be used: `unknown_ruleset` (unknown, or an internal candidate), `unsupported_profile`, `profile_undetected` or `unsupported_document_type`. The document was not validated.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"ValidationGone":{"content":{"application/problem+json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Problem"},{"properties":{"code":{"enum":["ruleset_disabled","result_gone","account_closed"]},"status":{"const":410}}}]}}},"description":"The selected release is withdrawn, the original saved result expired or was deleted, or the billing account is closed. Result replay never executes again.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"ValidationInternalError":{"content":{"application/problem+json":{"examples":{"internal":{"externalValue":"https://api.ironfang.com/finance/examples/v1-problem-validation-internal.json","summary":"An internal failure during validation"}},"schema":{"allOf":[{"$ref":"#/components/schemas/Problem"},{"properties":{"code":{"enum":["internal_error"]},"status":{"const":500}},"required":["outcome","usage_charged"]}]}}},"description":"Ironfang Finance failed while validating, in a way that is not the validator's own. Indeterminate: the body says `outcome: indeterminate` and `usage_charged: false`.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"ValidationRateLimited":{"content":{"application/problem+json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Problem"},{"properties":{"code":{"enum":["rate_limited","free_allowance_exhausted","account_budget_exhausted","product_budget_exhausted","exemption_limit_reached"]},"status":{"const":429}}}]}}},"description":"Per-address or per-key rate limit (`rate_limited`), which asks for a retry, or the billing account can admit no more of this work this month: the free allowance used up without paid usage, or a monthly limit reached.","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"},"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"ValidationTimeout":{"content":{"application/problem+json":{"examples":{"timeout":{"externalValue":"https://api.ironfang.com/finance/examples/v1-problem-indeterminate-timeout.json","summary":"Deadline exceeded"}},"schema":{"allOf":[{"$ref":"#/components/schemas/Problem"},{"properties":{"code":{"enum":["validation_timeout"]},"status":{"const":504}}}]}}},"description":"The 10-second validation deadline passed. Indeterminate: `outcome: indeterminate`, `usage_charged: false`.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"ValidatorUnavailable":{"content":{"application/problem+json":{"examples":{"unavailable":{"externalValue":"https://api.ironfang.com/finance/examples/v1-problem-indeterminate-unavailable.json","summary":"Validator dependency failure"}},"schema":{"allOf":[{"$ref":"#/components/schemas/Problem"},{"properties":{"code":{"enum":["validator_unavailable","billing_temporarily_unavailable"]},"status":{"const":503}}}]}}},"description":"The validator crashed, was unavailable, or an internal dependency failed. Indeterminate: `outcome: indeterminate`, `usage_charged: false`. Or billing was briefly unavailable (`billing_temporarily_unavailable`), and nothing was accepted. Retry after the indicated delay.","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"},"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}}},"schemas":{"BatchList":{"additionalProperties":false,"properties":{"batches":{"items":{"$ref":"#/components/schemas/BatchSummary"},"maxItems":100,"type":"array"},"next_cursor":{"type":"string"}},"required":["batches","next_cursor"],"type":"object"},"BatchRequest":{"additionalProperties":false,"properties":{"jobs":{"items":{"$ref":"#/components/schemas/JobRequest"},"maxItems":100,"minItems":1,"type":"array"}},"required":["jobs"],"type":"object"},"BatchResponse":{"additionalProperties":false,"properties":{"counts":{"additionalProperties":false,"properties":{"cancelled":{"maximum":100,"minimum":0,"type":"integer"},"completed":{"maximum":100,"minimum":0,"type":"integer"},"failed":{"maximum":100,"minimum":0,"type":"integer"},"queued":{"maximum":100,"minimum":0,"type":"integer"},"running":{"maximum":100,"minimum":0,"type":"integer"}},"required":["queued","running","completed","failed","cancelled"],"type":"object"},"created_at":{"format":"date-time","type":"string"},"id":{"$ref":"#/components/schemas/OpaqueID"},"jobs":{"items":{"$ref":"#/components/schemas/JobMetadata"},"maxItems":100,"minItems":1,"type":"array"},"schema":{"const":"financewolf/einvoice/batch/v1","type":"string"},"status":{"enum":["queued","running","completed","failed","cancelled","partial"],"type":"string"}},"required":["schema","id","status","created_at","counts","jobs"],"type":"object"},"BatchSummary":{"additionalProperties":false,"properties":{"counts":{"additionalProperties":false,"properties":{"cancelled":{"maximum":100,"minimum":0,"type":"integer"},"completed":{"maximum":100,"minimum":0,"type":"integer"},"failed":{"maximum":100,"minimum":0,"type":"integer"},"queued":{"maximum":100,"minimum":0,"type":"integer"},"running":{"maximum":100,"minimum":0,"type":"integer"}},"required":["queued","running","completed","failed","cancelled"],"type":"object"},"created_at":{"format":"date-time","type":"string"},"id":{"$ref":"#/components/schemas/OpaqueID"},"job_count":{"maximum":100,"minimum":0,"type":"integer"},"status":{"enum":["queued","running","completed","failed","cancelled","partial"],"type":"string"}},"required":["id","status","created_at","counts","job_count"],"type":"object"},"BillingRefusal":{"additionalProperties":false,"description":"On a billing refusal only: the product and meter the billing account refused, and when the free allowance renews (the start of the next UTC month) where that is the reason.","properties":{"meter":{"enum":["finance.validation","finance.generation"],"type":"string"},"product":{"enum":["finance"],"type":"string"},"reset_at":{"format":"date-time","type":"string"}},"type":"object"},"Delivery":{"additionalProperties":false,"properties":{"attempts":{"maximum":32,"minimum":0,"type":"integer"},"created_at":{"format":"date-time","type":"string"},"destination_id":{"format":"uuid","type":"string"},"event":{"$ref":"#/components/schemas/DeliveryEvent"},"event_id":{"format":"uuid","type":"string"},"expires_at":{"format":"date-time","type":"string"},"failure_code":{"enum":["destination_unavailable","credentials_unavailable","network_error","http_rejected","storage_rejected","object_conflict","result_unavailable","internal_error","lease_expired","event_expired"],"type":"string"},"finished_at":{"format":"date-time","type":"string"},"http_status":{"maximum":599,"minimum":1,"type":"integer"},"id":{"format":"uuid","type":"string"},"max_attempts":{"maximum":32,"minimum":1,"type":"integer"},"next_attempt_at":{"description":"Scheduled time for pending work; ignore for terminal deliveries.","format":"date-time","type":"string"},"operation_id":{"format":"uuid","type":"string"},"status":{"enum":["pending","delivering","delivered","failed"],"type":"string"}},"required":["id","event_id","operation_id","destination_id","status","attempts","max_attempts","created_at","expires_at","next_attempt_at","event"],"type":"object"},"DeliveryArtifact":{"additionalProperties":false,"properties":{"bytes":{"minimum":1,"type":"integer"},"content_type":{"enum":["application/json","application/xml"],"type":"string"},"name":{"enum":["result.json","document.xml"],"type":"string"},"sha256":{"$ref":"#/components/schemas/SHA256"}},"required":["name","content_type","sha256","bytes"],"type":"object"},"DeliveryAttempt":{"additionalProperties":false,"properties":{"attempt":{"maximum":32,"minimum":1,"type":"integer"},"failure_code":{"type":"string"},"finished_at":{"format":"date-time","type":"string"},"http_status":{"maximum":599,"minimum":1,"type":"integer"},"started_at":{"format":"date-time","type":"string"}},"required":["attempt","started_at"],"type":"object"},"DeliveryDetail":{"additionalProperties":false,"properties":{"attempts":{"maximum":32,"minimum":0,"type":"integer"},"created_at":{"format":"date-time","type":"string"},"destination_id":{"format":"uuid","type":"string"},"event":{"$ref":"#/components/schemas/DeliveryEvent"},"event_id":{"format":"uuid","type":"string"},"expires_at":{"format":"date-time","type":"string"},"failure_code":{"enum":["destination_unavailable","credentials_unavailable","network_error","http_rejected","storage_rejected","object_conflict","result_unavailable","internal_error","lease_expired","event_expired"],"type":"string"},"finished_at":{"format":"date-time","type":"string"},"history":{"items":{"$ref":"#/components/schemas/DeliveryAttempt"},"maxItems":32,"type":"array"},"http_status":{"maximum":599,"minimum":1,"type":"integer"},"id":{"format":"uuid","type":"string"},"max_attempts":{"maximum":32,"minimum":1,"type":"integer"},"next_attempt_at":{"description":"Scheduled time for pending work; ignore for terminal deliveries.","format":"date-time","type":"string"},"operation_id":{"format":"uuid","type":"string"},"status":{"enum":["pending","delivering","delivered","failed"],"type":"string"}},"required":["id","event_id","operation_id","destination_id","status","attempts","max_attempts","created_at","expires_at","next_attempt_at","event","history"],"type":"object"},"DeliveryEvent":{"additionalProperties":false,"properties":{"created_at":{"format":"date-time","type":"string"},"data":{"additionalProperties":false,"properties":{"artifacts":{"items":{"$ref":"#/components/schemas/DeliveryArtifact"},"maxItems":2,"type":"array"},"batch_id":{"format":"uuid","type":"string"},"failure_code":{"type":"string"},"input_sha256":{"$ref":"#/components/schemas/SHA256"},"job_id":{"format":"uuid","type":"string"},"operation":{"enum":["validate","generate"],"type":"string"},"operation_id":{"format":"uuid","type":"string"},"outcome":{"enum":["valid","invalid","indeterminate"],"type":"string"},"result_url":{"format":"uri-reference","type":"string"},"ruleset_id":{"type":"string"},"status":{"enum":["completed","error"],"type":"string"},"usage_charged":{"type":"boolean"}},"required":["operation_id","operation","status","outcome","input_sha256","usage_charged","artifacts"],"type":"object"},"id":{"format":"uuid","type":"string"},"schema":{"const":"financewolf/einvoice/event/v1","type":"string"},"state_version":{"const":1,"type":"integer"},"type":{"enum":["einvoice.validation.completed","einvoice.generation.completed","einvoice.operation.failed"],"type":"string"}},"required":["schema","id","type","created_at","state_version","data"],"type":"object"},"Destination":{"additionalProperties":false,"properties":{"config":{"$ref":"#/components/schemas/DestinationConfig"},"created_at":{"format":"date-time","type":"string"},"enabled":{"type":"boolean"},"id":{"format":"uuid","type":"string"},"name":{"maxLength":100,"minLength":1,"type":"string"},"retired_at":{"format":"date-time","type":"string"},"type":{"enum":["webhook","s3"],"type":"string"}},"required":["id","type","name","config","enabled","created_at"],"type":"object"},"DestinationConfig":{"additionalProperties":false,"properties":{"bucket":{"maxLength":63,"type":"string"},"endpoint":{"format":"uri","maxLength":2048,"type":"string"},"path_style":{"type":"boolean"},"prefix":{"maxLength":200,"type":"string"},"region":{"maxLength":63,"type":"string"},"url":{"format":"uri","maxLength":2048,"type":"string"}},"required":[],"type":"object"},"DestinationCreateRequest":{"additionalProperties":false,"properties":{"config":{"$ref":"#/components/schemas/DestinationConfig"},"credentials":{"$ref":"#/components/schemas/DestinationCredentials"},"name":{"maxLength":100,"minLength":1,"type":"string"},"type":{"enum":["webhook","s3"],"type":"string"}},"required":["type","name","config"],"type":"object"},"DestinationCreated":{"additionalProperties":false,"properties":{"config":{"$ref":"#/components/schemas/DestinationConfig"},"created_at":{"format":"date-time","type":"string"},"enabled":{"type":"boolean"},"id":{"format":"uuid","type":"string"},"name":{"maxLength":100,"minLength":1,"type":"string"},"retired_at":{"format":"date-time","type":"string"},"schema":{"const":"financewolf/einvoice/destination/v1","type":"string"},"signing_secret":{"description":"One-time hex-encoded 32-byte HMAC secret. Decode hex before verifying signatures.","pattern":"^[0-9a-f]{64}$","type":"string"},"type":{"enum":["webhook","s3"],"type":"string"}},"required":["id","type","name","config","enabled","created_at","schema"],"type":"object"},"DestinationCredentials":{"additionalProperties":false,"properties":{"access_key":{"maxLength":256,"minLength":1,"type":"string","writeOnly":true},"secret_key":{"maxLength":512,"minLength":1,"type":"string","writeOnly":true},"session_token":{"maxLength":8192,"type":"string","writeOnly":true}},"required":["access_key","secret_key"],"type":"object"},"DestinationUpdateRequest":{"additionalProperties":false,"minProperties":1,"properties":{"credentials":{"$ref":"#/components/schemas/DestinationCredentials"},"enabled":{"type":"boolean"},"rotate_secret":{"type":"boolean"}},"required":[],"type":"object"},"DocumentType":{"enum":["invoice","credit_note"],"type":"string"},"DocumentTypeSelector":{"default":"auto","enum":["invoice","credit_note","auto"],"type":"string"},"EngineInfo":{"additionalProperties":false,"description":"What ran, exactly, so the result can be reproduced.","properties":{"hint_pack_version":{"description":"The version of Ironfang's hint mappings applied, when any. Nonblank when present.","maxLength":32,"minLength":1,"type":"string"},"image_digest":{"$ref":"#/components/schemas/ImageDigest"},"name":{"description":"The validator engine and version, e.g. `phive/12.1.0`.","maxLength":64,"minLength":1,"type":"string"}},"required":["name","image_digest"],"type":"object"},"FieldError":{"additionalProperties":false,"properties":{"code":{"enum":["required","invalid_value","unknown_field","too_long"],"type":"string"},"field":{"description":"The option or parameter at fault, e.g. `ruleset`, `options.document_type`.","maxLength":128,"minLength":1,"type":"string"},"message":{"maxLength":256,"minLength":1,"type":"string"}},"required":["field","code","message"],"type":"object"},"Finding":{"additionalProperties":false,"allOf":[{"else":{"required":["location"]},"if":{"properties":{"location_kind":{"const":"none"}}},"then":{"not":{"required":["location"]}}},{"if":{"required":["hint"]},"then":{"required":["hint_code"]}}],"description":"One normalised rule result. The official rule id and severity are preserved; message and location are bounded and never carry invoice values. Hints are Ironfang's own, versioned by `engine.hint_pack_version`, and may be absent.","properties":{"hint":{"maxLength":512,"minLength":1,"type":"string"},"hint_code":{"pattern":"^[a-z0-9-]{1,80}$","type":"string"},"layer":{"$ref":"#/components/schemas/Layer"},"location":{"description":"A bounded, sanitised location. Absent when `location_kind` is `none`.","maxLength":256,"minLength":1,"type":"string"},"location_kind":{"$ref":"#/components/schemas/LocationKind"},"message":{"$ref":"#/components/schemas/FindingMessage"},"rule_id":{"description":"The official rule identifier, e.g. `PEPPOL-EN16931-R020`, `BR-CO-10`, or `cvc-complex-type.2.4.a` for schema errors.","maxLength":64,"minLength":1,"pattern":"^[A-Za-z0-9._:-]+$","type":"string"},"ruleset_id":{"$ref":"#/components/schemas/RulesetID"},"severity":{"$ref":"#/components/schemas/Severity"},"source_url":{"$ref":"#/components/schemas/HTTPSURL","description":"The official specification or rule source for this rule, when known."}},"required":["severity","layer","rule_id","message","location_kind","ruleset_id"],"type":"object"},"FindingCounts":{"additionalProperties":false,"properties":{"by_layer":{"additionalProperties":false,"properties":{"en16931":{"minimum":0,"type":"integer"},"input":{"minimum":0,"type":"integer"},"peppol":{"minimum":0,"type":"integer"},"xml":{"minimum":0,"type":"integer"},"xsd":{"minimum":0,"type":"integer"}},"required":["input","xml","xsd","en16931","peppol"],"type":"object"},"by_severity":{"additionalProperties":false,"properties":{"fatal":{"minimum":0,"type":"integer"},"warning":{"minimum":0,"type":"integer"}},"required":["fatal","warning"],"type":"object"}},"required":["by_severity","by_layer"],"type":"object"},"FindingMessage":{"description":"One of Ironfang's fixed finding messages, selected by the finding's rule id (for the validator's own FW-* rules) or by its layer. No message text from the validation engine or a rule set is ever returned: the rule id and the sanitised location carry the detail. The union below is exactly the closed catalogue (services/finance-validator/corpus/finding-messages.json); a result carrying any other message is refused before it is stored or written.","enum":["A Peppol BIS Billing 3.0 rule failed at this location.","An EN 16931 business rule failed at this location.","The document could not be read as XML within the contract limits.","The document does not conform to the UBL 2.1 schema at this location.","The document encoding, declaration or structure is not accepted.","The document has more elements than the contract limit of 250,000.","The document nests elements deeper than the contract limit of 64.","The input is empty or larger than the contract limit of 5,242,880 bytes.","The input was refused by the contract limits before parsing."],"type":"string"},"FindingsSummary":{"additionalProperties":false,"allOf":[{"if":{"properties":{"truncated":{"const":true}}},"then":{"properties":{"returned":{"const":1000}}}}],"description":"Truncation is explicit. `returned` is the length of `findings`; `total` is how many exist; `cap` is the inline maximum; `truncated` is `total \u003e returned`, and a truncated result returns exactly `cap`.","properties":{"cap":{"const":1000,"type":"integer"},"returned":{"maximum":1000,"minimum":0,"type":"integer"},"total":{"minimum":0,"type":"integer"},"truncated":{"type":"boolean"}},"required":["returned","total","cap","truncated"],"type":"object"},"GenerationDefAccount":{"additionalProperties":false,"properties":{"branch_id":{"$ref":"#/components/schemas/GenerationDefId"},"id":{"$ref":"#/components/schemas/GenerationDefId"},"name":{"$ref":"#/components/schemas/GenerationDefName"}},"required":["id"],"type":"object"},"GenerationDefAddress":{"additionalProperties":false,"properties":{"additional_street":{"$ref":"#/components/schemas/GenerationDefName"},"address_line":{"$ref":"#/components/schemas/GenerationDefName"},"city":{"$ref":"#/components/schemas/GenerationDefName"},"country":{"$ref":"#/components/schemas/GenerationDefCountry"},"postal_code":{"$ref":"#/components/schemas/GenerationDefId"},"region":{"$ref":"#/components/schemas/GenerationDefName"},"street":{"$ref":"#/components/schemas/GenerationDefName"}},"required":["country"],"type":"object"},"GenerationDefAmount":{"maxLength":23,"pattern":"^-?(0|[1-9][0-9]{0,14})(\\.[0-9]{1,2})?$","type":"string"},"GenerationDefContact":{"additionalProperties":false,"minProperties":1,"properties":{"email":{"$ref":"#/components/schemas/GenerationDefName"},"name":{"$ref":"#/components/schemas/GenerationDefName"},"telephone":{"$ref":"#/components/schemas/GenerationDefId"}},"required":[],"type":"object"},"GenerationDefCountry":{"pattern":"^[A-Z]{2}$","type":"string"},"GenerationDefDate":{"format":"date","pattern":"^[1-9][0-9]{3}-[0-9]{2}-[0-9]{2}$","type":"string"},"GenerationDefDecimal":{"maxLength":23,"pattern":"^-?(0|[1-9][0-9]{0,14})(\\.[0-9]{1,6})?$","type":"string"},"GenerationDefDelivery":{"additionalProperties":false,"properties":{"address":{"$ref":"#/components/schemas/GenerationDefAddress"},"date":{"$ref":"#/components/schemas/GenerationDefDate"}},"required":["date","address"],"type":"object"},"GenerationDefDocumentAdjustment":{"additionalProperties":false,"dependentRequired":{"base_amount":["percentage"],"percentage":["base_amount"]},"properties":{"amount":{"$ref":"#/components/schemas/GenerationDefUnsignedAmount"},"base_amount":{"$ref":"#/components/schemas/GenerationDefUnsignedAmount"},"percentage":{"$ref":"#/components/schemas/GenerationDefUnsignedDecimal"},"reason":{"$ref":"#/components/schemas/GenerationDefText"},"reason_code":{"$ref":"#/components/schemas/GenerationDefId"},"tax":{"$ref":"#/components/schemas/GenerationDefTax"}},"required":["amount","reason","tax"],"type":"object"},"GenerationDefEndpoint":{"additionalProperties":false,"properties":{"scheme":{"pattern":"^[0-9]{4}$","type":"string"},"value":{"$ref":"#/components/schemas/GenerationDefId"}},"required":["value","scheme"],"type":"object"},"GenerationDefId":{"allOf":[{"pattern":"\\S"},{"pattern":"^\\S(?:[\\s\\S]*\\S)?$"}],"maxLength":128,"minLength":1,"pattern":"^[^\u0000-\b\u000b\f\u000e-\u001f�-￿]*$","type":"string"},"GenerationDefIdentifier":{"additionalProperties":false,"properties":{"scheme":{"pattern":"^[A-Za-z0-9-]{1,16}$","type":"string"},"value":{"$ref":"#/components/schemas/GenerationDefId"}},"required":["value"],"type":"object"},"GenerationDefItem":{"additionalProperties":false,"properties":{"buyer_id":{"$ref":"#/components/schemas/GenerationDefId"},"description":{"$ref":"#/components/schemas/GenerationDefText"},"name":{"$ref":"#/components/schemas/GenerationDefName"},"seller_id":{"$ref":"#/components/schemas/GenerationDefId"},"standard_id":{"$ref":"#/components/schemas/GenerationDefStandardItemId"}},"required":["name"],"type":"object"},"GenerationDefLine":{"additionalProperties":false,"properties":{"accounting_cost":{"$ref":"#/components/schemas/GenerationDefId"},"allowances":{"items":{"$ref":"#/components/schemas/GenerationDefLineAdjustment"},"maxItems":100,"minItems":1,"type":"array"},"charges":{"items":{"$ref":"#/components/schemas/GenerationDefLineAdjustment"},"maxItems":100,"minItems":1,"type":"array"},"id":{"$ref":"#/components/schemas/GenerationDefId"},"item":{"$ref":"#/components/schemas/GenerationDefItem"},"net_amount":{"$ref":"#/components/schemas/GenerationDefAmount"},"note":{"$ref":"#/components/schemas/GenerationDefText"},"order_line_reference":{"$ref":"#/components/schemas/GenerationDefId"},"price_amount":{"$ref":"#/components/schemas/GenerationDefUnsignedDecimal"},"price_base_quantity":{"$ref":"#/components/schemas/GenerationDefUnsignedDecimal"},"quantity":{"$ref":"#/components/schemas/GenerationDefDecimal"},"tax":{"$ref":"#/components/schemas/GenerationDefTax"},"unit":{"pattern":"^[A-Z0-9]{2,3}$","type":"string"}},"required":["id","quantity","unit","price_amount","item","tax"],"type":"object"},"GenerationDefLineAdjustment":{"additionalProperties":false,"dependentRequired":{"base_amount":["percentage"],"percentage":["base_amount"]},"properties":{"amount":{"$ref":"#/components/schemas/GenerationDefUnsignedAmount"},"base_amount":{"$ref":"#/components/schemas/GenerationDefUnsignedAmount"},"percentage":{"$ref":"#/components/schemas/GenerationDefUnsignedDecimal"},"reason":{"$ref":"#/components/schemas/GenerationDefText"},"reason_code":{"$ref":"#/components/schemas/GenerationDefId"}},"required":["amount","reason"],"type":"object"},"GenerationDefName":{"allOf":[{"pattern":"\\S"}],"maxLength":256,"minLength":1,"pattern":"^[^\u0000-\b\u000b\f\u000e-\u001f�-￿]*$","type":"string"},"GenerationDefNote":{"allOf":[{"pattern":"\\S"}],"maxLength":2000,"minLength":1,"pattern":"^[^\u0000-\b\u000b\f\u000e-\u001f�-￿]*$","type":"string"},"GenerationDefParty":{"additionalProperties":false,"properties":{"address":{"$ref":"#/components/schemas/GenerationDefAddress"},"contact":{"$ref":"#/components/schemas/GenerationDefContact"},"endpoint":{"$ref":"#/components/schemas/GenerationDefEndpoint"},"identifier":{"$ref":"#/components/schemas/GenerationDefIdentifier"},"legal_identifier":{"$ref":"#/components/schemas/GenerationDefIdentifier"},"name":{"$ref":"#/components/schemas/GenerationDefName"},"tax_registration_identifier":{"$ref":"#/components/schemas/GenerationDefId"},"trading_name":{"$ref":"#/components/schemas/GenerationDefName"},"vat_identifier":{"$ref":"#/components/schemas/GenerationDefId"}},"required":["name","endpoint","address"],"type":"object"},"GenerationDefPayment":{"additionalProperties":false,"allOf":[{"else":{"not":{"required":["account"]}},"if":{"properties":{"code":{"enum":["30","58"]}}},"then":{"required":["account"]}}],"properties":{"account":{"$ref":"#/components/schemas/GenerationDefAccount"},"code":{"enum":["10","30","58","97"]},"reference":{"$ref":"#/components/schemas/GenerationDefId"}},"required":["code"],"type":"object"},"GenerationDefReference":{"additionalProperties":false,"properties":{"id":{"$ref":"#/components/schemas/GenerationDefId"},"issue_date":{"$ref":"#/components/schemas/GenerationDefDate"}},"required":["id"],"type":"object"},"GenerationDefStandardItemId":{"additionalProperties":false,"properties":{"scheme":{"pattern":"^[0-9]{4}$","type":"string"},"value":{"$ref":"#/components/schemas/GenerationDefId"}},"required":["value","scheme"],"type":"object"},"GenerationDefTax":{"additionalProperties":false,"allOf":[{"else":{"required":["rate"]},"if":{"properties":{"category":{"const":"O"}}},"then":{"not":{"required":["rate"]}}}],"properties":{"category":{"enum":["S","Z","E","AE","G","K","O","L","M"]},"exemption_code":{"pattern":"^VATEX-[A-Z0-9-]{1,32}$","type":"string"},"exemption_reason":{"$ref":"#/components/schemas/GenerationDefText"},"rate":{"$ref":"#/components/schemas/GenerationDefUnsignedDecimal"}},"required":["category"],"type":"object"},"GenerationDefTaxSubtotal":{"additionalProperties":false,"properties":{"tax":{"$ref":"#/components/schemas/GenerationDefTax"},"tax_amount":{"$ref":"#/components/schemas/GenerationDefAmount"},"taxable_amount":{"$ref":"#/components/schemas/GenerationDefAmount"}},"required":["tax","taxable_amount","tax_amount"],"type":"object"},"GenerationDefText":{"allOf":[{"pattern":"\\S"}],"maxLength":1024,"minLength":1,"pattern":"^[^\u0000-\b\u000b\f\u000e-\u001f�-￿]*$","type":"string"},"GenerationDefTotals":{"additionalProperties":false,"properties":{"allowance_total":{"$ref":"#/components/schemas/GenerationDefAmount"},"charge_total":{"$ref":"#/components/schemas/GenerationDefAmount"},"line_net_amount":{"$ref":"#/components/schemas/GenerationDefAmount"},"payable_amount":{"$ref":"#/components/schemas/GenerationDefAmount"},"prepaid_amount":{"$ref":"#/components/schemas/GenerationDefAmount"},"rounding_amount":{"$ref":"#/components/schemas/GenerationDefAmount"},"tax_amount":{"$ref":"#/components/schemas/GenerationDefAmount"},"tax_exclusive_amount":{"$ref":"#/components/schemas/GenerationDefAmount"},"tax_inclusive_amount":{"$ref":"#/components/schemas/GenerationDefAmount"}},"required":["line_net_amount","allowance_total","charge_total","tax_exclusive_amount","tax_amount","tax_inclusive_amount","prepaid_amount","rounding_amount","payable_amount"],"type":"object"},"GenerationDefUnsignedAmount":{"maxLength":23,"pattern":"^(0|[1-9][0-9]{0,14})(\\.[0-9]{1,2})?$","type":"string"},"GenerationDefUnsignedDecimal":{"maxLength":23,"pattern":"^(0|[1-9][0-9]{0,14})(\\.[0-9]{1,6})?$","type":"string"},"GenerationRequest":{"additionalProperties":false,"allOf":[{"if":{"properties":{"document_type":{"const":"credit_note"}}},"then":{"not":{"required":["due_date"]}}}],"anyOf":[{"required":["buyer_reference"]},{"required":["purchase_order_reference"]}],"description":"Deterministic UBL 2.1 Invoice (380) or CreditNote (381) input. Schema validation does not assert Peppol validity. Decimal strings are exact; amount reconciliation is an additional generator check.","properties":{"accounting_cost":{"$ref":"#/components/schemas/GenerationDefId"},"allowances":{"items":{"$ref":"#/components/schemas/GenerationDefDocumentAdjustment"},"maxItems":100,"minItems":1,"type":"array"},"buyer":{"$ref":"#/components/schemas/GenerationDefParty"},"buyer_reference":{"$ref":"#/components/schemas/GenerationDefId"},"charges":{"items":{"$ref":"#/components/schemas/GenerationDefDocumentAdjustment"},"maxItems":100,"minItems":1,"type":"array"},"contract_reference":{"$ref":"#/components/schemas/GenerationDefId"},"currency":{"pattern":"^[A-Z]{3}$","type":"string"},"delivery":{"$ref":"#/components/schemas/GenerationDefDelivery"},"document_type":{"enum":["invoice","credit_note"]},"due_date":{"$ref":"#/components/schemas/GenerationDefDate"},"id":{"$ref":"#/components/schemas/GenerationDefId"},"input_schema_version":{"const":"financewolf/einvoice/generation-input/v1"},"issue_date":{"$ref":"#/components/schemas/GenerationDefDate"},"lines":{"items":{"$ref":"#/components/schemas/GenerationDefLine"},"maxItems":2000,"minItems":1,"type":"array"},"note":{"$ref":"#/components/schemas/GenerationDefNote"},"payment":{"$ref":"#/components/schemas/GenerationDefPayment"},"payment_terms":{"$ref":"#/components/schemas/GenerationDefText"},"preceding_invoices":{"items":{"$ref":"#/components/schemas/GenerationDefReference"},"maxItems":100,"minItems":1,"type":"array"},"prepaid_amount":{"$ref":"#/components/schemas/GenerationDefUnsignedAmount"},"purchase_order_reference":{"$ref":"#/components/schemas/GenerationDefId"},"rounding_amount":{"$ref":"#/components/schemas/GenerationDefAmount"},"seller":{"$ref":"#/components/schemas/GenerationDefParty"},"tax_point_date":{"$ref":"#/components/schemas/GenerationDefDate"},"tax_subtotals":{"items":{"$ref":"#/components/schemas/GenerationDefTaxSubtotal"},"maxItems":100,"minItems":1,"type":"array"},"totals":{"$ref":"#/components/schemas/GenerationDefTotals"}},"required":["input_schema_version","document_type","id","issue_date","currency","seller","buyer","lines"],"title":"Ironfang Finance e-invoice generation input v1","type":"object"},"GenerationResult":{"additionalProperties":false,"dependentRequired":{"operation_id":["usage"],"usage":["operation_id"]},"properties":{"artifact":{"additionalProperties":false,"properties":{"bytes":{"maximum":5242880,"minimum":1,"type":"integer"},"content_type":{"const":"application/xml","type":"string"},"data_base64":{"contentEncoding":"base64","maxLength":6990508,"minLength":4,"pattern":"^[A-Za-z0-9+/]+={0,2}$","type":"string"},"sha256":{"pattern":"^[0-9a-f]{64}$","type":"string"}},"required":["content_type","sha256","bytes","data_base64"],"type":"object"},"generator":{"additionalProperties":false,"properties":{"layer":{"const":"generator","type":"string"},"status":{"const":"passed","type":"string"},"version":{"pattern":"^financewolf-ubl/[0-9]+[.][0-9]+[.][0-9]+$","type":"string"}},"required":["version","layer","status"],"type":"object"},"input":{"additionalProperties":false,"properties":{"bytes":{"maximum":31457280,"minimum":0,"type":"integer"},"canonicalization":{"const":"financewolf-json/1","type":"string"},"content_type":{"const":"application/json","type":"string"},"document_type":{"enum":["invoice","credit_note"],"type":"string"},"schema_version":{"const":"financewolf/einvoice/generation-input/v1","type":"string"},"sha256":{"$ref":"#/components/schemas/SHA256","description":"SHA-256 of the exact bytes received."}},"required":["sha256","bytes","content_type","document_type","canonicalization","schema_version"],"type":"object"},"operation_id":{"$ref":"#/components/schemas/OpaqueID"},"outcome":{"const":"valid","type":"string"},"schema":{"const":"financewolf/einvoice/generation-result/v1","type":"string"},"status":{"const":"completed","type":"string"},"usage":{"$ref":"#/components/schemas/Usage"},"validation":{"allOf":[{"$ref":"#/components/schemas/ValidationVerdict"},{"properties":{"layers":{"items":{"properties":{"status":{"const":"passed","type":"string"}}}},"outcome":{"const":"valid","type":"string"},"profile":{"properties":{"source":{"const":"requested","type":"string"}}}}}]}},"required":["schema","status","outcome","input","generator","artifact","validation"],"type":"object"},"HTTPSURL":{"description":"A provenance URL under one narrow grammar: https, a lowercase DNS host of at least two labels, no userinfo, no IP literal, an optional port 1..65535, an optional path, query and fragment of unreserved and sub-delimiter characters or valid percent-escapes, no whitespace. The same expression is held by the registry review, the database and this document.","format":"uri","pattern":"^https://[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*\\.[a-z]([a-z0-9-]{0,61}[a-z0-9])?(:([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))?(/([A-Za-z0-9._~!$\u0026()*+,;=:@/-]|%[0-9A-Fa-f]{2})*)?(\\?([A-Za-z0-9._~!$\u0026()*+,;=:@/?-]|%[0-9A-Fa-f]{2})*)?(#([A-Za-z0-9._~!$\u0026()*+,;=:@/?-]|%[0-9A-Fa-f]{2})*)?$","type":"string"},"ImageDigest":{"description":"The pinned validator engine image, as a sha256 digest.","pattern":"^sha256:[0-9a-f]{64}$","type":"string"},"JobList":{"additionalProperties":false,"properties":{"jobs":{"items":{"$ref":"#/components/schemas/JobResponse"},"maxItems":100,"type":"array"},"next_cursor":{"type":"string"}},"required":["jobs","next_cursor"],"type":"object"},"JobMetadata":{"additionalProperties":false,"properties":{"attempts":{"maximum":3,"minimum":0,"type":"integer"},"batch_id":{"$ref":"#/components/schemas/OpaqueID"},"created_at":{"format":"date-time","type":"string"},"engine_image_digest":{"pattern":"^sha256:[0-9a-f]{64}$","type":"string"},"expires_at":{"format":"date-time","type":"string"},"failure_code":{"enum":["validation_timeout","validator_unavailable","internal_error","generation_failed","job_cancelled","job_expired"],"type":"string"},"finished_at":{"format":"date-time","type":"string"},"id":{"$ref":"#/components/schemas/OpaqueID"},"operation":{"enum":["validate","generate"],"type":"string"},"operation_id":{"$ref":"#/components/schemas/OpaqueID"},"outcome":{"enum":["valid","invalid","indeterminate"],"type":"string"},"position":{"maximum":99,"minimum":0,"type":"integer"},"result_available":{"type":"boolean"},"ruleset_id":{"$ref":"#/components/schemas/RulesetID"},"status":{"enum":["queued","running","completed","failed","cancelled"],"type":"string"},"usage_charged":{"type":"boolean"}},"required":["id","operation_id","position","operation","status","attempts","ruleset_id","engine_image_digest","usage_charged","result_available","created_at","expires_at"],"type":"object"},"JobRequest":{"oneOf":[{"additionalProperties":false,"properties":{"document_base64":{"contentEncoding":"base64","maxLength":6990508,"minLength":4,"type":"string"},"operation":{"const":"validate","type":"string"},"options":{"$ref":"#/components/schemas/ValidationOptions"}},"required":["operation","document_base64"],"type":"object"},{"additionalProperties":false,"properties":{"input":{"$ref":"#/components/schemas/GenerationRequest"},"operation":{"const":"generate","type":"string"},"options":{"allOf":[{"$ref":"#/components/schemas/ValidationOptions"},{"properties":{"document_type":{"enum":["auto"]}},"required":["ruleset","profile"]}]}},"required":["operation","input","options"],"type":"object"}]},"JobResponse":{"additionalProperties":false,"properties":{"attempts":{"maximum":3,"minimum":0,"type":"integer"},"batch_id":{"$ref":"#/components/schemas/OpaqueID"},"created_at":{"format":"date-time","type":"string"},"engine_image_digest":{"pattern":"^sha256:[0-9a-f]{64}$","type":"string"},"expires_at":{"format":"date-time","type":"string"},"failure_code":{"enum":["validation_timeout","validator_unavailable","internal_error","generation_failed","job_cancelled","job_expired"],"type":"string"},"finished_at":{"format":"date-time","type":"string"},"id":{"$ref":"#/components/schemas/OpaqueID"},"operation":{"enum":["validate","generate"],"type":"string"},"operation_id":{"$ref":"#/components/schemas/OpaqueID"},"outcome":{"enum":["valid","invalid","indeterminate"],"type":"string"},"position":{"maximum":99,"minimum":0,"type":"integer"},"result_available":{"type":"boolean"},"result_url":{"format":"uri-reference","type":"string"},"ruleset_id":{"$ref":"#/components/schemas/RulesetID"},"schema":{"const":"financewolf/einvoice/job/v1","type":"string"},"status":{"enum":["queued","running","completed","failed","cancelled"],"type":"string"},"usage_charged":{"type":"boolean"}},"required":["schema","id","operation_id","position","operation","status","attempts","ruleset_id","engine_image_digest","usage_charged","result_available","created_at","expires_at"],"type":"object"},"Layer":{"description":"A validation layer, in execution order.","enum":["input","xml","xsd","en16931","peppol"],"type":"string"},"LayerReason":{"description":"Why a layer did not run to a verdict. `skipped_after_failure`: an earlier layer failed and this one cannot execute safely. `not_applicable`: the layer does not exist for the selected profile. `engine_error`: the validator failed inside this layer. `deadline_exceeded`: the deadline passed during this layer.","enum":["skipped_after_failure","not_applicable","engine_error","deadline_exceeded"],"type":"string"},"LayerResult":{"additionalProperties":false,"allOf":[{"else":{"not":{"required":["reason"]}},"if":{"properties":{"status":{"enum":["skipped","error"]}}},"then":{"properties":{"fatal":{"const":0},"warning":{"const":0}},"required":["reason"]}},{"if":{"properties":{"status":{"const":"failed"}}},"then":{"properties":{"fatal":{"minimum":1}}}},{"if":{"properties":{"status":{"const":"passed"}}},"then":{"properties":{"fatal":{"const":0}}}}],"properties":{"fatal":{"description":"Fatal findings this layer produced.","minimum":0,"type":"integer"},"layer":{"$ref":"#/components/schemas/Layer"},"reason":{"$ref":"#/components/schemas/LayerReason","description":"Required when `status` is `skipped` or `error`; absent otherwise."},"status":{"$ref":"#/components/schemas/LayerStatus"},"warning":{"minimum":0,"type":"integer"}},"required":["layer","status","fatal","warning"],"type":"object"},"LayerStatus":{"enum":["passed","failed","skipped","error"],"type":"string"},"LicenceStatus":{"description":"Ironfang's licence review of a rule artefact: `permissive`, `notice-required` (attribution travels with derived output), or `no-raw-redistribution` (Ironfang Finance executes it; raw files are not served).","enum":["permissive","notice-required","no-raw-redistribution"],"type":"string"},"LocationKind":{"description":"The vocabulary the validator and the findings store use: `none`, `xpath`, or `line-column`.","enum":["none","xpath","line-column"],"type":"string"},"OpaqueID":{"description":"An opaque Ironfang identifier.","maxLength":64,"minLength":1,"pattern":"^[A-Za-z0-9_-]+$","type":"string"},"OperationStatus":{"description":"How far execution got, independent of the verdict.","enum":["completed","error"],"type":"string"},"Outcome":{"description":"The standard's verdict. `indeterminate` means Ironfang Finance could not decide; it is never converted to `invalid` and never billed.","enum":["valid","invalid","indeterminate"],"type":"string"},"Problem":{"additionalProperties":false,"allOf":[{"if":{"properties":{"code":{"const":"malformed_request"}}},"then":{"properties":{"status":{"const":400},"type":{"const":"https://ironfang.com/problems/malformed_request"}}}},{"if":{"properties":{"code":{"const":"unauthenticated"}}},"then":{"properties":{"status":{"const":401},"type":{"const":"https://ironfang.com/problems/unauthenticated"}}}},{"if":{"properties":{"code":{"const":"insufficient_scope"}}},"then":{"properties":{"status":{"const":403},"type":{"const":"https://ironfang.com/problems/insufficient_scope"}}}},{"if":{"properties":{"code":{"const":"not_found"}}},"then":{"properties":{"status":{"const":404},"type":{"const":"https://ironfang.com/problems/not_found"}}}},{"if":{"properties":{"code":{"const":"idempotency_conflict"}}},"then":{"properties":{"status":{"const":409},"type":{"const":"https://ironfang.com/problems/idempotency_conflict"}}}},{"if":{"properties":{"code":{"const":"result_gone"}}},"then":{"properties":{"status":{"const":410},"type":{"const":"https://ironfang.com/problems/result_gone"}}}},{"if":{"properties":{"code":{"const":"ruleset_disabled"}}},"then":{"properties":{"status":{"const":410},"type":{"const":"https://ironfang.com/problems/ruleset_disabled"}}}},{"if":{"properties":{"code":{"const":"payload_too_large"}}},"then":{"properties":{"status":{"const":413},"type":{"const":"https://ironfang.com/problems/payload_too_large"}}}},{"if":{"properties":{"code":{"const":"unsupported_content_type"}}},"then":{"properties":{"status":{"const":415},"type":{"const":"https://ironfang.com/problems/unsupported_content_type"}}}},{"if":{"properties":{"code":{"const":"unknown_ruleset"}}},"then":{"properties":{"status":{"const":422},"type":{"const":"https://ironfang.com/problems/unknown_ruleset"}}}},{"if":{"properties":{"code":{"const":"unsupported_profile"}}},"then":{"properties":{"status":{"const":422},"type":{"const":"https://ironfang.com/problems/unsupported_profile"}}}},{"if":{"properties":{"code":{"const":"profile_undetected"}}},"then":{"properties":{"status":{"const":422},"type":{"const":"https://ironfang.com/problems/profile_undetected"}}}},{"if":{"properties":{"code":{"const":"unsupported_document_type"}}},"then":{"properties":{"status":{"const":422},"type":{"const":"https://ironfang.com/problems/unsupported_document_type"}}}},{"if":{"properties":{"code":{"const":"rate_limited"}}},"then":{"properties":{"status":{"const":429},"type":{"const":"https://ironfang.com/problems/rate_limited"}}}},{"if":{"properties":{"code":{"const":"billing_moved"}}},"then":{"properties":{"status":{"const":410},"type":{"const":"https://ironfang.com/problems/billing_moved"}}}},{"if":{"properties":{"code":{"const":"free_allowance_exhausted"}}},"then":{"properties":{"status":{"const":429},"type":{"const":"https://ironfang.com/problems/free_allowance_exhausted"}}}},{"if":{"properties":{"code":{"const":"account_budget_exhausted"}}},"then":{"properties":{"status":{"const":429},"type":{"const":"https://ironfang.com/problems/account_budget_exhausted"}}}},{"if":{"properties":{"code":{"const":"product_budget_exhausted"}}},"then":{"properties":{"status":{"const":429},"type":{"const":"https://ironfang.com/problems/product_budget_exhausted"}}}},{"if":{"properties":{"code":{"const":"exemption_limit_reached"}}},"then":{"properties":{"status":{"const":429},"type":{"const":"https://ironfang.com/problems/exemption_limit_reached"}}}},{"if":{"properties":{"code":{"const":"paid_usage_paused"}}},"then":{"properties":{"status":{"const":402},"type":{"const":"https://ironfang.com/problems/paid_usage_paused"}}}},{"if":{"properties":{"code":{"const":"payment_required"}}},"then":{"properties":{"status":{"const":402},"type":{"const":"https://ironfang.com/problems/payment_required"}}}},{"if":{"properties":{"code":{"const":"payment_action_required"}}},"then":{"properties":{"status":{"const":402},"type":{"const":"https://ironfang.com/problems/payment_action_required"}}}},{"if":{"properties":{"code":{"const":"account_restricted"}}},"then":{"properties":{"status":{"const":402},"type":{"const":"https://ironfang.com/problems/account_restricted"}}}},{"if":{"properties":{"code":{"const":"account_closed"}}},"then":{"properties":{"status":{"const":410},"type":{"const":"https://ironfang.com/problems/account_closed"}}}},{"if":{"properties":{"code":{"const":"product_not_eligible"}}},"then":{"properties":{"status":{"const":403},"type":{"const":"https://ironfang.com/problems/product_not_eligible"}}}},{"if":{"properties":{"code":{"const":"billing_temporarily_unavailable"}}},"then":{"properties":{"status":{"const":503},"type":{"const":"https://ironfang.com/problems/billing_temporarily_unavailable"}}}},{"if":{"properties":{"code":{"const":"quota_exhausted"}}},"then":{"properties":{"status":{"const":429},"type":{"const":"https://ironfang.com/problems/quota_exhausted"}}}},{"if":{"properties":{"code":{"const":"report_unavailable"}}},"then":{"properties":{"status":{"const":503},"type":{"const":"https://ironfang.com/problems/report_unavailable"}}}},{"if":{"properties":{"code":{"const":"report_incomplete"}}},"then":{"properties":{"status":{"const":422},"type":{"const":"https://ironfang.com/problems/report_incomplete"}}}},{"if":{"properties":{"code":{"const":"render_unavailable"}}},"then":{"properties":{"status":{"const":503},"type":{"const":"https://ironfang.com/problems/render_unavailable"}}}},{"if":{"properties":{"code":{"const":"render_timeout"}}},"then":{"properties":{"status":{"const":504},"type":{"const":"https://ironfang.com/problems/render_timeout"}}}},{"if":{"properties":{"code":{"const":"generation_failed"}}},"then":{"properties":{"status":{"const":500},"type":{"const":"https://ironfang.com/problems/generation_failed"}},"required":["outcome","usage_charged"]}},{"if":{"properties":{"code":{"const":"internal_error"}}},"then":{"properties":{"status":{"const":500},"type":{"const":"https://ironfang.com/problems/internal_error"}}}},{"if":{"properties":{"code":{"const":"delivery_unavailable"}}},"then":{"properties":{"status":{"const":503},"type":{"const":"https://ironfang.com/problems/delivery_unavailable"}}}},{"if":{"properties":{"code":{"const":"jobs_unavailable"}}},"then":{"properties":{"status":{"const":503},"type":{"const":"https://ironfang.com/problems/jobs_unavailable"}}}},{"if":{"properties":{"code":{"const":"validator_unavailable"}}},"then":{"properties":{"status":{"const":503},"type":{"const":"https://ironfang.com/problems/validator_unavailable"}}}},{"if":{"properties":{"code":{"const":"validation_timeout"}}},"then":{"properties":{"status":{"const":504},"type":{"const":"https://ironfang.com/problems/validation_timeout"}}}},{"if":{"properties":{"code":{"enum":["validation_timeout","validator_unavailable"]}}},"then":{"required":["outcome","usage_charged"]}},{"if":{"not":{"properties":{"code":{"enum":["validation_timeout","validator_unavailable","internal_error","generation_failed"]}}}},"then":{"not":{"anyOf":[{"required":["outcome"]},{"required":["usage_charged"]},{"required":["operation_id"]}]}}},{"if":{"anyOf":[{"required":["outcome"]},{"required":["usage_charged"]},{"required":["operation_id"]}]},"then":{"required":["outcome","usage_charged"]}},{"if":{"required":["errors"]},"then":{"properties":{"code":{"const":"malformed_request"}}}},{"if":{"required":["retry_after_seconds"]},"then":{"properties":{"code":{"enum":["rate_limited","quota_exhausted","validator_unavailable","jobs_unavailable","delivery_unavailable","billing_temporarily_unavailable"]}}}},{"if":{"required":["billing"]},"then":{"properties":{"code":{"enum":["free_allowance_exhausted","account_budget_exhausted","product_budget_exhausted","exemption_limit_reached","paid_usage_paused","payment_required","payment_action_required","account_restricted","account_closed","product_not_eligible","billing_temporarily_unavailable"]}}}}],"description":"An RFC 9457 problem. `code` is the stable, machine-readable Ironfang identifier; `type` is its URL and `status` its one HTTP status, both fixed per code below. `detail` is safe to show and never carries parser output, invoice content or stack traces. Validation findings are never Problems.","properties":{"billing":{"$ref":"#/components/schemas/BillingRefusal"},"code":{"description":"The stable Ironfang problem code.","enum":["malformed_request","unauthenticated","insufficient_scope","idempotency_conflict","payload_too_large","unsupported_content_type","unknown_ruleset","ruleset_disabled","result_gone","unsupported_profile","profile_undetected","unsupported_document_type","not_found","rate_limited","quota_exhausted","billing_moved","free_allowance_exhausted","account_budget_exhausted","product_budget_exhausted","exemption_limit_reached","paid_usage_paused","payment_required","payment_action_required","account_restricted","account_closed","product_not_eligible","billing_temporarily_unavailable","validation_timeout","validator_unavailable","jobs_unavailable","delivery_unavailable","generation_failed","report_unavailable","report_incomplete","render_unavailable","render_timeout","internal_error"],"type":"string"},"detail":{"maxLength":512,"minLength":1,"type":"string"},"errors":{"description":"Field-level errors, on `malformed_request` only. Bounded; empty is omitted.","items":{"$ref":"#/components/schemas/FieldError"},"maxItems":50,"minItems":1,"type":"array"},"instance":{"format":"uri-reference","maxLength":256,"type":"string"},"operation_id":{"$ref":"#/components/schemas/OpaqueID","description":"On an indeterminate validation problem only, the operation record that was opened, if any."},"outcome":{"const":"indeterminate","type":"string"},"request_id":{"$ref":"#/components/schemas/RequestID"},"retry_after_seconds":{"description":"Only on problems that invite a retry (`rate_limited`, `quota_exhausted`, `validator_unavailable`, `billing_temporarily_unavailable`).","maximum":3600,"minimum":1,"type":"integer"},"status":{"maximum":599,"minimum":400,"type":"integer"},"title":{"maxLength":128,"minLength":1,"type":"string"},"type":{"format":"uri","pattern":"^https://ironfang\\.com/problems/[a-z_]+$","type":"string"},"usage_charged":{"const":false,"type":"boolean"}},"required":["type","title","status","detail","code","request_id"],"type":"object"},"ProfileDetection":{"additionalProperties":false,"description":"The profile family the document was validated as, and how that was decided.","properties":{"customization_id":{"description":"The document's `CustomizationID`, a profile identifier - not invoice content. Present when parsed.","maxLength":256,"type":"string"},"family":{"$ref":"#/components/schemas/ProfileFamily"},"profile_id":{"description":"The document's `ProfileID`. Present when parsed.","maxLength":256,"type":"string"},"source":{"description":"`requested` (the request named the profile or an exact ruleset) or `detected` (from the document's identifiers).","enum":["requested","detected"],"type":"string"}},"required":["family","source"],"type":"object"},"ProfileFamily":{"description":"A supported profile family. V1 supports Peppol BIS Billing 3.","enum":["peppol-bis-billing-3"],"type":"string"},"RenderRequest":{"additionalProperties":false,"properties":{"operation_id":{"$ref":"#/components/schemas/OpaqueID"}},"required":["operation_id"],"type":"object"},"ReportKeySet":{"additionalProperties":false,"properties":{"keys":{"items":{"additionalProperties":false,"properties":{"algorithm":{"const":"Ed25519"},"id":{"pattern":"^fw_ed25519_[0-9a-f]{64}$","type":"string"},"public_key":{"description":"Unpadded standard base64 of the 32-byte public key.","maxLength":43,"minLength":43,"type":"string"},"status":{"enum":["active","retired","revoked"]}},"required":["id","algorithm","public_key","status"],"type":"object"},"maxItems":32,"type":"array"},"schema":{"const":"financewolf/einvoice/report-keys/v1"}},"required":["schema","keys"],"type":"object"},"ReportRequest":{"additionalProperties":false,"properties":{"include_pdf":{"default":false,"type":"boolean"},"operation_id":{"format":"uuid","type":"string"}},"required":["operation_id"],"type":"object"},"ReportVerification":{"additionalProperties":false,"properties":{"artifact_hashes":{"enum":["not_verified","verified","invalid"]},"claim":{"type":"string"},"errors":{"items":{"type":"string"},"type":"array"},"invoice_outcome":{"enum":["valid","invalid"]},"key_id":{"pattern":"^fw_ed25519_[0-9a-f]{64}$","type":"string"},"manifest_sha256":{"pattern":"^[0-9a-f]{64}$","type":"string"},"operation_id":{"format":"uuid","type":"string"},"ruleset_id":{"type":"string"},"schema":{"const":"financewolf/einvoice/report-verification/v1"},"signature":{"enum":["not_verified","verified","invalid","untrusted"]},"timestamp":{"enum":["not_present","not_verified","verified","invalid"]},"verified":{"type":"boolean"}},"required":["schema","verified","signature","artifact_hashes","timestamp","errors","claim"],"type":"object"},"RequestID":{"pattern":"^[A-Za-z0-9_-]{8,64}$","type":"string"},"ResultLinks":{"additionalProperties":false,"description":"Authorised links, present only on authenticated results whose retention policy keeps them. Time-limited.","properties":{"evidence":{"$ref":"#/components/schemas/HTTPSURL"},"report":{"$ref":"#/components/schemas/HTTPSURL"}},"type":"object"},"RetainedResultList":{"additionalProperties":false,"properties":{"next_cursor":{"maxLength":256,"minLength":1,"type":"string"},"results":{"items":{"additionalProperties":false,"properties":{"available":{"type":"boolean"},"created_at":{"format":"date-time","type":"string"},"deleted_at":{"format":"date-time","type":"string"},"display_name":{"description":"Optional portal filename; removed with the hosted result.","maxLength":255,"type":"string"},"expires_at":{"format":"date-time","type":"string"},"kind":{"enum":["validate","generate"],"type":"string"},"operation_id":{"$ref":"#/components/schemas/OpaqueID"},"outcome":{"enum":["valid","invalid"],"type":"string"},"ruleset_id":{"pattern":"^fwrs_[a-z0-9_]{1,80}$","type":"string"}},"required":["operation_id","outcome","ruleset_id","created_at","expires_at","available"],"type":"object"},"maxItems":100,"type":"array"},"retention_days":{"const":30,"type":"integer"},"total":{"description":"All matching metadata records, including expired/deleted results, independent of cursor.","format":"int64","minimum":0,"type":"integer"}},"required":["results","retention_days","total"],"type":"object"},"Ruleset":{"additionalProperties":false,"allOf":[{"if":{"properties":{"latest":{"const":true}}},"then":{"properties":{"state":{"const":"sendable"}}}},{"if":{"properties":{"state":{"const":"historical"}}},"then":{"properties":{"retired_at":{"$ref":"#/components/schemas/Timestamp"}},"required":["retired_at"]}}],"description":"The customer-safe identity of one immutable ruleset: everything needed to select it and to reproduce a validation that named it. Internal storage, review records and operator identities are not part of this contract.","properties":{"artifacts":{"allOf":[{"contains":{"properties":{"layer":{"const":"xsd"}}}},{"contains":{"properties":{"layer":{"const":"en16931"}}}},{"contains":{"properties":{"layer":{"const":"peppol"}}}}],"description":"Exactly one checksummed rule artefact per layer - xsd, en16931 and peppol.","items":{"$ref":"#/components/schemas/RulesetArtifact"},"maxItems":3,"minItems":3,"type":"array"},"bis_release":{"maxLength":32,"minLength":1,"type":"string"},"document_type":{"$ref":"#/components/schemas/DocumentType"},"en16931_release":{"maxLength":32,"minLength":1,"type":"string"},"engine":{"$ref":"#/components/schemas/EngineInfo"},"id":{"$ref":"#/components/schemas/RulesetID"},"latest":{"description":"True on the one `sendable` release `ruleset: latest` currently resolves to for this profile family and document type.","type":"boolean"},"official_release":{"description":"The standards body's release name.","maxLength":128,"minLength":1,"type":"string"},"profile_family":{"$ref":"#/components/schemas/ProfileFamily"},"published_on":{"description":"The standards body's publication date, when known.","oneOf":[{"format":"date","type":"string"},{"type":"null"}]},"replaced_by":{"description":"The release that superseded this one, when retired.","oneOf":[{"$ref":"#/components/schemas/RulesetID"},{"type":"null"}]},"retired_at":{"description":"When Ironfang stopped sending against this release. Distinct from the official window.","oneOf":[{"$ref":"#/components/schemas/Timestamp"},{"type":"null"}]},"runtime":{"$ref":"#/components/schemas/RulesetRuntime"},"schema":{"const":"financewolf/einvoice/ruleset/v1","type":"string"},"sources":{"description":"Official sources for the release. Never empty.","items":{"$ref":"#/components/schemas/HTTPSURL"},"minItems":1,"type":"array"},"state":{"$ref":"#/components/schemas/RulesetState"},"syntax":{"enum":["UBL 2.1"],"type":"string"},"valid_from":{"$ref":"#/components/schemas/Timestamp","description":"When the official release takes effect. Part of the release's identity; never changes."},"valid_until":{"description":"The official end of validity, or null when open-ended. Never changes.","oneOf":[{"$ref":"#/components/schemas/Timestamp"},{"type":"null"}]},"vesid":{"maxLength":128,"minLength":1,"type":"string"}},"required":["schema","id","state","latest","profile_family","document_type","syntax","official_release","en16931_release","bis_release","vesid","engine","valid_from","valid_until","sources","artifacts"],"type":"object"},"RulesetArtifact":{"additionalProperties":false,"description":"One rule artefact's checksum and provenance. Where the files live is not part of the contract.","properties":{"layer":{"enum":["xsd","en16931","peppol"],"type":"string"},"licence":{"description":"The artefact's own licence, as an SPDX identifier or `NOASSERTION`.","maxLength":64,"minLength":1,"type":"string"},"licence_status":{"$ref":"#/components/schemas/LicenceStatus"},"sha256":{"$ref":"#/components/schemas/SHA256"},"source_url":{"$ref":"#/components/schemas/HTTPSURL"}},"required":["layer","sha256","licence","licence_status","source_url"],"type":"object"},"RulesetID":{"description":"An immutable ruleset identifier. Never reused, never edited.","pattern":"^fwrs_[a-z0-9_]{1,80}$","type":"string"},"RulesetList":{"additionalProperties":false,"properties":{"rulesets":{"description":"Empty, never null.","items":{"$ref":"#/components/schemas/Ruleset"},"type":"array"},"schema":{"const":"financewolf/einvoice/ruleset-list/v1","type":"string"}},"required":["schema","rulesets"],"type":"object"},"RulesetRuntime":{"additionalProperties":false,"description":"Metadata observed from the running validator's installed dependencies and registered VES. Omitted for retained engines without runtime reporting or before a successful health observation. observed_at is not a readiness guarantee. Library versions are not Peppol or EN 16931 specification versions. Upstream deprecation/effective dates describe the VES; state/latest describe Ironfang Finance's independently managed release lifecycle.","properties":{"name":{"maxLength":256,"minLength":1,"type":"string"},"observed_at":{"format":"date-time","type":"string"},"phive_rules_peppol_version":{"maxLength":32,"pattern":"^[0-9][0-9A-Za-z.+-]{0,31}$","type":"string"},"phive_rules_version":{"maxLength":32,"pattern":"^[0-9][0-9A-Za-z.+-]{0,31}$","type":"string"},"phive_version":{"maxLength":32,"pattern":"^[0-9][0-9A-Za-z.+-]{0,31}$","type":"string"},"saxon_version":{"maxLength":32,"pattern":"^[0-9][0-9A-Za-z.+-]{0,31}$","type":"string"},"upstream_deprecated":{"type":"boolean"},"upstream_valid_from":{"format":"date-time","type":"string"},"upstream_valid_until":{"format":"date-time","type":"string"},"validator_version":{"maxLength":32,"pattern":"^[0-9][0-9A-Za-z.+-]{0,31}$","type":"string"}},"required":["validator_version","phive_version","phive_rules_version","phive_rules_peppol_version","saxon_version","name","upstream_deprecated","observed_at"],"type":"object"},"RulesetSelection":{"additionalProperties":false,"description":"What was asked for and what was used.","properties":{"id":{"$ref":"#/components/schemas/RulesetID","description":"The immutable ruleset the document was validated against."},"official_release":{"maxLength":128,"type":"string"},"requested":{"$ref":"#/components/schemas/RulesetSelector","description":"The selector the request carried (defaulted to `latest` if absent). An exact selector equals `id`."},"selection_method":{"$ref":"#/components/schemas/SelectionMethod"},"state":{"$ref":"#/components/schemas/RulesetState","description":"The release's state at validation time. `latest` and `detected` always report `sendable`; `exact` reports whichever validatable state the release is in."},"vesid":{"description":"The engine's coordinate for the release, for cross-referencing official tooling.","maxLength":128,"type":"string"}},"required":["requested","id","selection_method","state"],"type":"object"},"RulesetSelector":{"default":"latest","description":"`latest`, or an exact ruleset id in any validatable state.","pattern":"^(latest|fwrs_[a-z0-9_]{1,80})$","type":"string"},"RulesetState":{"description":"Public lifecycle state of a release that may be validated against. `sendable` is current and is what `latest` resolves to; `scheduled` is the next release, not yet in force, validatable for migration testing; `historical` has been superseded but stays validatable for reproduction. Withdrawn (`disabled`) releases answer `410` and never appear in a body; candidate releases are internal and never appear.","enum":["scheduled","sendable","historical"],"type":"string"},"SHA256":{"description":"Lowercase hex SHA-256.","pattern":"^[0-9a-f]{64}$","type":"string"},"SelectionMethod":{"description":"How the ruleset was chosen: `exact` (the request named the id), `latest` (resolved from the requested profile), `detected` (profile detected from the document, then resolved as latest).","enum":["exact","latest","detected"],"type":"string"},"Severity":{"description":"`fatal` findings make the document invalid; `warning` findings never do and are never promoted.","enum":["fatal","warning"],"type":"string"},"Timestamp":{"description":"RFC 3339, UTC.","format":"date-time","type":"string"},"Usage":{"additionalProperties":false,"description":"Present only on authenticated results. Never reported for anonymous validation, which has no entitlement to report against.","properties":{"charged":{"description":"Whether this validation consumed entitlement. Always `false` for indeterminate outcomes.","type":"boolean"},"credits":{"description":"Units of the operation's meter this operation used - 1 when charged, otherwise 0.","minimum":0,"type":"integer"},"period_ends_at":{"$ref":"#/components/schemas/Timestamp","description":"Only on results recorded before usage billing, returned as they were recorded."},"remaining":{"description":"Only on results recorded before usage billing, returned as they were recorded. The month's usage is read at GET /v1/einvoices/usage.","minimum":0,"type":"integer"}},"required":["charged","credits"],"type":"object"},"UsageReport":{"additionalProperties":false,"properties":{"billing":{"const":"/billing/v1","description":"Where the account's usage, limits, payment and invoices are managed.","type":"string"},"catalogue_version":{"description":"The billing catalogue that prices the month; empty when unmetered.","type":"string"},"currency":{"const":"GBP"},"metered":{"description":"False where unified billing is off; nothing is charged and `meters` is empty.","type":"boolean"},"meters":{"items":{"additionalProperties":false,"properties":{"billable":{"minimum":0,"type":"integer"},"charge":{"description":"The month's charge so far in pounds, before tax.","pattern":"^[0-9]+[.][0-9]{2,8}$","type":"string"},"exempt":{"type":"boolean"},"free_allowance":{"minimum":0,"type":"integer"},"free_used":{"minimum":0,"type":"integer"},"meter":{"enum":["finance.validation","finance.generation"],"type":"string"},"operation":{"enum":["validate","generate"],"type":"string"},"remaining_free":{"minimum":0,"type":"integer"},"reserved":{"minimum":0,"type":"integer"},"unit":{"type":"string"},"used":{"minimum":0,"type":"integer"}},"required":["meter","operation","unit","free_allowance","used","free_used","billable","reserved","remaining_free","charge","exempt"],"type":"object"},"type":"array"},"mode":{"description":"The billing account's mode; empty when unmetered.","enum":["","free","setup_pending","enabled","paused","closed"],"type":"string"},"operations":{"additionalProperties":false,"properties":{"charged_operations":{"minimum":0,"type":"integer"},"groups":{"items":{"additionalProperties":false,"properties":{"admission":{"description":"`usage` for operations the billing account admitted; the retired plan key for older ones.","type":"string"},"event_type":{"enum":["einvoice.validation","einvoice.generation"]},"quantity":{"minimum":0,"type":"integer"},"source":{"type":"string"}},"required":["admission","source","event_type","quantity"],"type":"object"},"type":"array"},"in_progress_operations":{"minimum":0,"type":"integer"},"mismatches":{"minimum":0,"type":"integer"},"period_end":{"format":"date-time","type":"string"},"period_start":{"format":"date-time","type":"string"},"usage_events":{"minimum":0,"type":"integer"}},"required":["period_start","period_end","charged_operations","usage_events","in_progress_operations","mismatches","groups"],"type":"object"},"paid_eligible":{"description":"Whether the organisation's business is approved for paid Finance usage.","type":"boolean"},"period_end":{"format":"date-time","type":"string"},"period_start":{"format":"date-time","type":"string"},"product":{"const":"finance"}},"required":["product","metered","period_start","period_end","catalogue_version","mode","paid_eligible","currency","meters","billing","operations"],"type":"object"},"ValidationInput":{"additionalProperties":false,"properties":{"bytes":{"maximum":5242880,"minimum":0,"type":"integer"},"content_type":{"enum":["application/xml","text/xml"],"type":"string"},"document_type":{"description":"The document type found at the root element; `unknown` when the root was not recognised or the XML did not parse.","enum":["invoice","credit_note","unknown"],"type":"string"},"sha256":{"$ref":"#/components/schemas/SHA256","description":"SHA-256 of the exact bytes received."}},"required":["sha256","bytes","content_type","document_type"],"type":"object"},"ValidationOptions":{"additionalProperties":false,"description":"Options for the multipart form. Unknown fields are rejected so a misspelt option cannot silently fall back to a default.","properties":{"document_type":{"$ref":"#/components/schemas/DocumentTypeSelector"},"profile":{"$ref":"#/components/schemas/ProfileFamily"},"ruleset":{"$ref":"#/components/schemas/RulesetSelector"}},"type":"object"},"ValidationResult":{"additionalProperties":false,"allOf":[{"else":{"properties":{"outcome":{"const":"indeterminate"}}},"if":{"properties":{"status":{"const":"completed"}}},"then":{"properties":{"outcome":{"enum":["valid","invalid"]}}}},{"if":{"properties":{"outcome":{"const":"valid"}}},"then":{"properties":{"counts":{"properties":{"by_severity":{"properties":{"fatal":{"const":0}}}}},"layers":{"items":{"properties":{"status":{"enum":["passed","skipped"]}}}}}}},{"if":{"properties":{"outcome":{"const":"invalid"}}},"then":{"properties":{"counts":{"properties":{"by_severity":{"properties":{"fatal":{"minimum":1}}}}},"layers":{"contains":{"properties":{"status":{"const":"failed"}}},"items":{"properties":{"status":{"enum":["passed","failed","skipped"]}}}}}}},{"if":{"properties":{"outcome":{"const":"indeterminate"}}},"then":{"properties":{"layers":{"contains":{"properties":{"status":{"const":"error"}}}},"usage":{"properties":{"charged":{"const":false}}}}}},{"if":{"anyOf":[{"required":["usage"]},{"required":["document_id"]},{"required":["links"]}]},"then":{"required":["operation_id"]}},{"if":{"properties":{"ruleset":{"properties":{"selection_method":{"const":"exact"}}}}},"then":{"properties":{"profile":{"properties":{"source":{"const":"requested"}}},"ruleset":{"properties":{"requested":{"pattern":"^fwrs_"},"state":{"enum":["scheduled","sendable","historical"]}}}}}},{"if":{"properties":{"ruleset":{"properties":{"requested":{"pattern":"^fwrs_"}}}}},"then":{"properties":{"ruleset":{"properties":{"selection_method":{"const":"exact"}}}}}},{"if":{"properties":{"ruleset":{"properties":{"selection_method":{"enum":["latest","detected"]}}}}},"then":{"properties":{"ruleset":{"properties":{"requested":{"const":"latest"},"state":{"const":"sendable"}}}}}},{"if":{"properties":{"ruleset":{"properties":{"selection_method":{"const":"latest"}}}}},"then":{"properties":{"profile":{"properties":{"source":{"const":"requested"}}}}}},{"if":{"properties":{"ruleset":{"properties":{"selection_method":{"const":"detected"}}}}},"then":{"properties":{"profile":{"properties":{"source":{"const":"detected"}}}}}},{"if":{"properties":{"findings":{"contains":{"required":["hint"]}}}},"then":{"properties":{"engine":{"required":["hint_pack_version"]}}}}],"description":"The general result model: what a validation produced, whether or not it reached a verdict. Retained deliberately broad so a stored or asynchronous operation (later slices) can represent an indeterminate run. The synchronous `200` response uses the narrower `ValidationVerdict`.","properties":{"counts":{"$ref":"#/components/schemas/FindingCounts"},"document_id":{"$ref":"#/components/schemas/OpaqueID","description":"Present when the authenticated organisation's retention policy stored the document. Absent otherwise."},"engine":{"$ref":"#/components/schemas/EngineInfo"},"findings":{"description":"Normalised findings in deterministic order. Empty, never null.","items":{"$ref":"#/components/schemas/Finding"},"maxItems":1000,"type":"array"},"findings_summary":{"$ref":"#/components/schemas/FindingsSummary"},"input":{"$ref":"#/components/schemas/ValidationInput"},"layers":{"description":"Every layer, always in this order, always present.","items":{"$ref":"#/components/schemas/LayerResult"},"maxItems":5,"minItems":5,"prefixItems":[{"allOf":[{"$ref":"#/components/schemas/LayerResult"},{"properties":{"layer":{"const":"input"}}}]},{"allOf":[{"$ref":"#/components/schemas/LayerResult"},{"properties":{"layer":{"const":"xml"}}}]},{"allOf":[{"$ref":"#/components/schemas/LayerResult"},{"properties":{"layer":{"const":"xsd"}}}]},{"allOf":[{"$ref":"#/components/schemas/LayerResult"},{"properties":{"layer":{"const":"en16931"}}}]},{"allOf":[{"$ref":"#/components/schemas/LayerResult"},{"properties":{"layer":{"const":"peppol"}}}]}],"type":"array"},"links":{"$ref":"#/components/schemas/ResultLinks"},"operation_id":{"$ref":"#/components/schemas/OpaqueID","description":"Present for authenticated requests, which create an operation record. Absent for anonymous validation."},"outcome":{"$ref":"#/components/schemas/Outcome"},"profile":{"$ref":"#/components/schemas/ProfileDetection"},"ruleset":{"$ref":"#/components/schemas/RulesetSelection"},"schema":{"const":"financewolf/einvoice/validation-result/v1","description":"The version of this result document.","type":"string"},"status":{"$ref":"#/components/schemas/OperationStatus"},"timing":{"$ref":"#/components/schemas/ValidationTiming"},"usage":{"$ref":"#/components/schemas/Usage"}},"required":["schema","status","outcome","input","ruleset","profile","layers","counts","findings","findings_summary","timing","engine"],"type":"object"},"ValidationTiming":{"additionalProperties":false,"properties":{"completed_at":{"$ref":"#/components/schemas/Timestamp"},"duration_ms":{"description":"Wall time, bounded by the 10-second deadline.","maximum":10000,"minimum":0,"type":"integer"},"started_at":{"$ref":"#/components/schemas/Timestamp"}},"required":["started_at","completed_at","duration_ms"],"type":"object"},"ValidationVerdict":{"allOf":[{"$ref":"#/components/schemas/ValidationResult"},{"properties":{"layers":{"items":{"properties":{"status":{"enum":["passed","failed","skipped"]}}}},"outcome":{"enum":["valid","invalid"]},"status":{"const":"completed"}}}],"description":"The `200` response of the validate operation: a `ValidationResult` that reached a verdict. `status` is `completed`, `outcome` is `valid` or `invalid`, and no layer is in `error`. Indeterminate results are not representable here; they are Problems."},"XMLDocument":{"contentMediaType":"application/xml","description":"A UBL 2.1 Invoice or CreditNote document, at most 5 MiB.","maxLength":5242880,"type":"string"}},"securitySchemes":{"platformKey":{"description":"An Ironfang platform API key minted in the portal. Scopes listed on an operation are the ones the key must carry. Keys are the only credential this boundary accepts in V1.","scheme":"bearer","type":"http"}}},"externalDocs":{"description":"Ironfang Finance documentation","url":"https://ironfang.com/docs/finance"},"info":{"contact":{"email":"hello@ironfang.com","name":"Ironfang","url":"https://ironfang.com/finance"},"description":"Ironfang Finance 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 Ironfang Finance 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\nBilling belongs to the organisation's Ironfang billing account at /billing/v1;\nFinance's own usage is GET /v1/einvoices/usage. The retired plan and\nsubscription read, GET /v1/einvoices/billing, answers `410 billing_moved`\nto every request.\n\n**Authentication.** The V1 boundary accepts Ironfang platform API keys\nonly (`Authorization: Bearer \u003ckey\u003e`), minted in the Ironfang portal with\nIronfang Finance 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**Usage and billing.** Every authenticated validation and generation is\nadmitted by the organisation's Ironfang billing account before it runs:\none unit of `finance.validation` or `finance.generation`, each with its\nown free monthly allowance, charged when the rules run to a verdict (a\ngeneration when its document passes its own validation; that validation\nand the readable PDF are part of it) and returned otherwise. A replayed\nIdempotency-Key is never admitted again. Beyond the free allowance, paid\nusage needs the account's paid usage enabled and an approved business;\nrefusals are the billing problems (`free_allowance_exhausted`,\n`payment_required`, `product_not_eligible` and the rest), the same codes\nevery Ironfang product answers with. Anonymous requests are never\nmetered.\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\n**For agents and tools.** The canonical base URL is\nhttps://api.ironfang.com/finance and every operation has a stable\n`operationId`. Every error an operation returns is an RFC 9457 Problem\n(`application/problem+json`): branch on `code` and `status`; `type` is the\ncode's own URL under https://ironfang.com/problems/ and `request_id` is the\nvalue to quote to support. The machine-readable index of the Ironfang APIs\nis https://ironfang.com/.well-known/api-catalog, and\nhttps://ironfang.com/llms.txt is a plain-text map of them for a language\nmodel. The MCP server at https://mcp.ironfang.com/mcp (see\nhttps://ironfang.com/docs/mcp) carries reference tools only for this\nproduct - rule and ruleset lookup; validating or generating a document is\ndone with this REST API. Financewolf is the name the product launched\nunder, not a separate product. It survives in the `financewolf/...` schema\nand canonicalisation identifiers and the `financewolf-einvoicing` product\nvalue, which are recorded in signed evidence and do not change. Its\n`/financewolf` prefix and `X-Financewolf-*`/`Financewolf-*` headers were\nretired on 28 September 2026, and the prefix now answers `410`.\n","license":{"identifier":"LicenseRef-Ironfang-Proprietary","name":"Proprietary"},"summary":"Generate UBL invoices and validate Invoice or CreditNote XML with PHIVE.","termsOfService":"https://ironfang.com/legal/terms","title":"Ironfang Finance E-Invoicing API","version":"1.4.0"},"openapi":"3.1.0","paths":{"/v1/einvoices/batches":{"get":{"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. Only V1 jobs and batches are listed, read or cancelled here; one made through /finance/v2 is not found, and V2 lists both at /finance/v2/einvoices/jobs and /finance/v2/einvoices/batches.","operationId":"listEinvoiceBatches","parameters":[{"in":"query","name":"limit","schema":{"default":50,"maximum":100,"minimum":1,"type":"integer"}},{"description":"Pass next_cursor from the preceding page; empty means the first page.","in":"query","name":"cursor","schema":{"maxLength":256,"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchList"}}},"description":"One bounded page."},"400":{"$ref":"#/components/responses/MalformedRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/InsufficientScope"},"429":{"$ref":"#/components/responses/ReadRateLimited"},"500":{"$ref":"#/components/responses/ReadInternalError"}},"security":[{"platformKey":[]}],"summary":"List batches","tags":["jobs"],"x-ironfang-scopes":["finance:einvoices:read"]},"post":{"description":"Accept work durably using finance: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.","operationId":"createEinvoiceBatch","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchResponse"}}},"description":"Current metadata for an idempotent replay.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchResponse"}}},"description":"Accepted durably; no validation outcome yet.","headers":{"Location":{"description":"Relative polling URL.","schema":{"format":"uri-reference","type":"string"}},"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"400":{"$ref":"#/components/responses/MalformedRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/ScopeOrEligibility"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"410":{"$ref":"#/components/responses/AdmissionGone"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"415":{"$ref":"#/components/responses/UnsupportedContentType"},"422":{"$ref":"#/components/responses/UnsupportedSelection"},"429":{"content":{"application/problem+json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Problem"},{"not":{"anyOf":[{"required":["outcome"]},{"required":["usage_charged"]},{"required":["operation_id"]}]},"properties":{"code":{"enum":["rate_limited","quota_exhausted","free_allowance_exhausted","account_budget_exhausted","product_budget_exhausted","exemption_limit_reached"]},"status":{"const":429}}}]}}},"description":"Rate limit, tenant backlog limit (`quota_exhausted`; retry after work finishes or is cancelled), or the billing account can admit no more of this work this month.","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"},"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"500":{"$ref":"#/components/responses/ReadInternalError"},"503":{"content":{"application/problem+json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Problem"},{"not":{"anyOf":[{"required":["outcome"]},{"required":["usage_charged"]},{"required":["operation_id"]}]},"properties":{"code":{"enum":["jobs_unavailable","billing_temporarily_unavailable"]},"status":{"const":503}}}]}}},"description":"New async admission unavailable, or billing briefly unavailable; retry with the same Idempotency-Key. Existing metadata remains readable.","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"},"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}}},"security":[{"platformKey":[]}],"summary":"Create an ordered batch","tags":["jobs"],"x-ironfang-scopes":["finance:einvoices:write"]}},"/v1/einvoices/batches/{id}":{"get":{"description":"Read tenant-owned job metadata with finance: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.","operationId":"getEinvoiceBatch","parameters":[{"in":"path","name":"id","required":true,"schema":{"$ref":"#/components/schemas/OpaqueID"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchResponse"}}},"description":"Current metadata.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"400":{"$ref":"#/components/responses/MalformedRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/InsufficientScope"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/ReadRateLimited"},"500":{"$ref":"#/components/responses/ReadInternalError"}},"security":[{"platformKey":[]}],"summary":"Get an ordered batch","tags":["jobs"],"x-ironfang-scopes":["finance:einvoices:read"]}},"/v1/einvoices/batches/{id}/cancel":{"post":{"description":"Cancel unfinished tenant-owned work with finance: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.","operationId":"cancelEinvoiceBatch","parameters":[{"in":"path","name":"id","required":true,"schema":{"$ref":"#/components/schemas/OpaqueID"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchResponse"}}},"description":"Current metadata.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"400":{"$ref":"#/components/responses/MalformedRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/InsufficientScope"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/ReadRateLimited"},"500":{"$ref":"#/components/responses/ReadInternalError"}},"security":[{"platformKey":[]}],"summary":"Cancel an ordered batch","tags":["jobs"],"x-ironfang-scopes":["finance:einvoices:write"]}},"/v1/einvoices/deliveries":{"get":{"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. Destination, operation and status filters apply before pagination. Pending selects pending deliveries without attempts; retrying selects pending deliveries with at least one attempt. Only deliveries of V1 events are listed here; an operation made through /finance/v2 queues a V2 event, whose deliveries V2 lists at /finance/v2/einvoices/deliveries together with these.","operationId":"listEinvoiceDeliveries","parameters":[{"in":"query","name":"destination","schema":{"format":"uuid","type":"string"}},{"in":"query","name":"operation_id","schema":{"format":"uuid","type":"string"}},{"in":"query","name":"status","schema":{"enum":["pending","retrying","delivering","delivered","failed"],"type":"string"}},{"in":"query","name":"cursor","schema":{"maxLength":256,"type":"string"}},{"in":"query","name":"limit","schema":{"default":50,"maximum":100,"minimum":1,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":false,"properties":{"deliveries":{"items":{"$ref":"#/components/schemas/Delivery"},"maxItems":100,"type":"array"},"next_cursor":{"type":"string"}},"required":["deliveries","next_cursor"],"type":"object"}}},"description":"Success. All responses use Cache-Control: no-store.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"400":{"$ref":"#/components/responses/MalformedRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/InsufficientScope"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/DeliveryRateLimited"},"500":{"$ref":"#/components/responses/ReadInternalError"},"503":{"$ref":"#/components/responses/DeliveryUnavailable"}},"security":[{"platformKey":[]}],"summary":"List delivery history with keyset pagination","tags":["deliveries"],"x-ironfang-scopes":["finance:einvoices:read"]}},"/v1/einvoices/deliveries/{id}":{"get":{"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.","operationId":"getEinvoiceDelivery","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeliveryDetail"}}},"description":"Success. All responses use Cache-Control: no-store.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"400":{"$ref":"#/components/responses/MalformedRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/InsufficientScope"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/DeliveryRateLimited"},"500":{"$ref":"#/components/responses/ReadInternalError"},"503":{"$ref":"#/components/responses/DeliveryUnavailable"}},"security":[{"platformKey":[]}],"summary":"Read a delivery and its immutable attempt history","tags":["deliveries"],"x-ironfang-scopes":["finance:einvoices:read"]}},"/v1/einvoices/deliveries/{id}/retry":{"post":{"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.","operationId":"retryEinvoiceDelivery","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeliveryDetail"}}},"description":"Success. All responses use Cache-Control: no-store.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"400":{"$ref":"#/components/responses/MalformedRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/InsufficientScope"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/DeliveryRateLimited"},"500":{"$ref":"#/components/responses/ReadInternalError"},"503":{"$ref":"#/components/responses/DeliveryUnavailable"}},"security":[{"platformKey":[]}],"summary":"Retry failed delivery without rerunning validation","tags":["deliveries"],"x-ironfang-scopes":["finance:einvoices:destinations:manage"]}},"/v1/einvoices/destinations":{"get":{"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.","operationId":"listEinvoiceDestinations","responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":false,"properties":{"destinations":{"items":{"$ref":"#/components/schemas/Destination"},"maxItems":10,"type":"array"}},"required":["destinations"],"type":"object"}}},"description":"Success. All responses use Cache-Control: no-store.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"400":{"$ref":"#/components/responses/MalformedRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/InsufficientScope"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/DeliveryRateLimited"},"500":{"$ref":"#/components/responses/ReadInternalError"},"503":{"$ref":"#/components/responses/DeliveryUnavailable"}},"security":[{"platformKey":[]}],"summary":"List active destination configurations","tags":["deliveries"],"x-ironfang-scopes":["finance:einvoices:destinations:manage"]},"post":{"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.","operationId":"createEinvoiceDestination","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DestinationCreateRequest"}}},"description":"One JSON object, maximum 16 KiB. Unknown, duplicate and null fields are rejected. Credentials are encrypted and never returned; webhook create/rotate returns its signing secret once.","required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DestinationCreated"}}},"description":"Success. All responses use Cache-Control: no-store.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"400":{"$ref":"#/components/responses/MalformedRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/InsufficientScope"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/DeliveryRateLimited"},"500":{"$ref":"#/components/responses/ReadInternalError"},"503":{"$ref":"#/components/responses/DeliveryUnavailable"}},"security":[{"platformKey":[]}],"summary":"Create a webhook or S3 destination","tags":["deliveries"],"x-ironfang-scopes":["finance:einvoices:destinations:manage"]}},"/v1/einvoices/destinations/{id}":{"delete":{"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.","operationId":"deleteEinvoiceDestination","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"204":{"description":"Success. All responses use Cache-Control: no-store.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"400":{"$ref":"#/components/responses/MalformedRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/InsufficientScope"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/DeliveryRateLimited"},"500":{"$ref":"#/components/responses/ReadInternalError"},"503":{"$ref":"#/components/responses/DeliveryUnavailable"}},"security":[{"platformKey":[]}],"summary":"Retire a destination and erase its stored credentials","tags":["deliveries"],"x-ironfang-scopes":["finance:einvoices:destinations:manage"]},"get":{"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.","operationId":"getEinvoiceDestination","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Destination"}}},"description":"Success. All responses use Cache-Control: no-store.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"400":{"$ref":"#/components/responses/MalformedRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/InsufficientScope"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/DeliveryRateLimited"},"500":{"$ref":"#/components/responses/ReadInternalError"},"503":{"$ref":"#/components/responses/DeliveryUnavailable"}},"security":[{"platformKey":[]}],"summary":"Read a destination without its credentials","tags":["deliveries"],"x-ironfang-scopes":["finance:einvoices:destinations:manage"]},"patch":{"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.","operationId":"updateEinvoiceDestination","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DestinationUpdateRequest"}}},"description":"One JSON object, maximum 16 KiB. Unknown, duplicate and null fields are rejected. Credentials are encrypted and never returned; webhook create/rotate returns its signing secret once.","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DestinationCreated"}}},"description":"Success. All responses use Cache-Control: no-store.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"400":{"$ref":"#/components/responses/MalformedRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/InsufficientScope"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/DeliveryRateLimited"},"500":{"$ref":"#/components/responses/ReadInternalError"},"503":{"$ref":"#/components/responses/DeliveryUnavailable"}},"security":[{"platformKey":[]}],"summary":"Enable, disable or rotate destination credentials","tags":["deliveries"],"x-ironfang-scopes":["finance:einvoices:destinations:manage"]}},"/v1/einvoices/generate":{"post":{"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 finance: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.","operationId":"generateEInvoice","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"in":"query","name":"ruleset","required":true,"schema":{"$ref":"#/components/schemas/RulesetSelector"}},{"in":"query","name":"profile","required":true,"schema":{"enum":["peppol-bis-billing-3"],"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationResult"}}},"description":"Only a fully validated artifact, after atomic authenticated persistence.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"400":{"$ref":"#/components/responses/MalformedRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/ScopeOrEligibility"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"410":{"$ref":"#/components/responses/ValidationGone"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"415":{"$ref":"#/components/responses/UnsupportedContentType"},"422":{"content":{"application/problem+json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Problem"},{"properties":{"code":{"enum":["unknown_ruleset","unsupported_profile","unsupported_document_type"]},"status":{"const":422}}}]}}},"description":"Generation selection refused.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"429":{"$ref":"#/components/responses/ValidationRateLimited"},"500":{"content":{"application/problem+json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Problem"},{"properties":{"code":{"enum":["internal_error","generation_failed"]},"status":{"const":500}},"required":["outcome","usage_charged"]}]}}},"description":"Indeterminate generation failure; no artifact is returned and nothing is charged.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"503":{"$ref":"#/components/responses/ValidatorUnavailable"},"504":{"$ref":"#/components/responses/ValidationTimeout"}},"security":[{},{"platformKey":[]}],"summary":"Generate and validate one UBL e-invoice","tags":["generation"],"x-ironfang-scopes":["finance:einvoices:write"]}},"/v1/einvoices/jobs":{"get":{"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. Only V1 jobs and batches are listed, read or cancelled here; one made through /finance/v2 is not found, and V2 lists both at /finance/v2/einvoices/jobs and /finance/v2/einvoices/batches.","operationId":"listEinvoiceJobs","parameters":[{"in":"query","name":"limit","schema":{"default":50,"maximum":100,"minimum":1,"type":"integer"}},{"description":"Pass next_cursor from the preceding page; empty means the first page.","in":"query","name":"cursor","schema":{"maxLength":256,"type":"string"}},{"in":"query","name":"status","schema":{"enum":["queued","running","completed","failed","cancelled"],"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobList"}}},"description":"One bounded page."},"400":{"$ref":"#/components/responses/MalformedRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/InsufficientScope"},"429":{"$ref":"#/components/responses/ReadRateLimited"},"500":{"$ref":"#/components/responses/ReadInternalError"}},"security":[{"platformKey":[]}],"summary":"List jobs","tags":["jobs"],"x-ironfang-scopes":["finance:einvoices:read"]},"post":{"description":"Accept work durably using finance: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.","operationId":"createEinvoiceJob","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobResponse"}}},"description":"Current metadata for an idempotent replay.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobResponse"}}},"description":"Accepted durably; no validation outcome yet.","headers":{"Location":{"description":"Relative polling URL.","schema":{"format":"uri-reference","type":"string"}},"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"400":{"$ref":"#/components/responses/MalformedRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/ScopeOrEligibility"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"410":{"$ref":"#/components/responses/AdmissionGone"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"415":{"$ref":"#/components/responses/UnsupportedContentType"},"422":{"$ref":"#/components/responses/UnsupportedSelection"},"429":{"content":{"application/problem+json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Problem"},{"not":{"anyOf":[{"required":["outcome"]},{"required":["usage_charged"]},{"required":["operation_id"]}]},"properties":{"code":{"enum":["rate_limited","quota_exhausted","free_allowance_exhausted","account_budget_exhausted","product_budget_exhausted","exemption_limit_reached"]},"status":{"const":429}}}]}}},"description":"Rate limit, tenant backlog limit (`quota_exhausted`; retry after work finishes or is cancelled), or the billing account can admit no more of this work this month.","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"},"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"500":{"$ref":"#/components/responses/ReadInternalError"},"503":{"content":{"application/problem+json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Problem"},{"not":{"anyOf":[{"required":["outcome"]},{"required":["usage_charged"]},{"required":["operation_id"]}]},"properties":{"code":{"enum":["jobs_unavailable","billing_temporarily_unavailable"]},"status":{"const":503}}}]}}},"description":"New async admission unavailable, or billing briefly unavailable; retry with the same Idempotency-Key. Existing metadata remains readable.","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"},"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}}},"security":[{"platformKey":[]}],"summary":"Create an async job","tags":["jobs"],"x-ironfang-scopes":["finance:einvoices:write"]}},"/v1/einvoices/jobs/{id}":{"get":{"description":"Read tenant-owned job metadata with finance: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.","operationId":"getEinvoiceJob","parameters":[{"in":"path","name":"id","required":true,"schema":{"$ref":"#/components/schemas/OpaqueID"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobResponse"}}},"description":"Current metadata.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"400":{"$ref":"#/components/responses/MalformedRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/InsufficientScope"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/ReadRateLimited"},"500":{"$ref":"#/components/responses/ReadInternalError"}},"security":[{"platformKey":[]}],"summary":"Get an async job","tags":["jobs"],"x-ironfang-scopes":["finance:einvoices:read"]}},"/v1/einvoices/jobs/{id}/cancel":{"post":{"description":"Cancel unfinished tenant-owned work with finance: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.","operationId":"cancelEinvoiceJob","parameters":[{"in":"path","name":"id","required":true,"schema":{"$ref":"#/components/schemas/OpaqueID"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobResponse"}}},"description":"Current metadata.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"400":{"$ref":"#/components/responses/MalformedRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/InsufficientScope"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/ReadRateLimited"},"500":{"$ref":"#/components/responses/ReadInternalError"}},"security":[{"platformKey":[]}],"summary":"Cancel an async job","tags":["jobs"],"x-ironfang-scopes":["finance:einvoices:write"]}},"/v1/einvoices/render":{"post":{"description":"Requires finance: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","operationId":"renderEinvoice","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenderRequest"}}},"required":true},"responses":{"200":{"content":{"application/pdf":{"schema":{"format":"binary","type":"string"}}},"description":"Readable representation of the exact retained generated XML, as an attachment.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Ironfang-Source-SHA256":{"description":"SHA-256 of the authoritative XML.","schema":{"pattern":"^[0-9a-f]{64}$","type":"string"}},"X-Ironfang-Template-Version":{"description":"Ironfang Finance readable template version.","schema":{"type":"string"}},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"400":{"$ref":"#/components/responses/MalformedRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/InsufficientScope"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"$ref":"#/components/responses/ResultGone"},"415":{"$ref":"#/components/responses/UnsupportedContentType"},"429":{"$ref":"#/components/responses/ReadRateLimited"},"500":{"$ref":"#/components/responses/ReadInternalError"},"503":{"content":{"application/problem+json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Problem"},{"not":{"anyOf":[{"required":["outcome"]},{"required":["usage_charged"]},{"required":["operation_id"]}]},"properties":{"code":{"enum":["render_unavailable"]},"status":{"const":503}}}]}}},"description":"Readable PDF engine unavailable. The original validation result and usage are unchanged.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"504":{"content":{"application/problem+json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Problem"},{"not":{"anyOf":[{"required":["outcome"]},{"required":["usage_charged"]},{"required":["operation_id"]}]},"properties":{"code":{"enum":["render_timeout"]},"status":{"const":504}}}]}}},"description":"Readable PDF processing deadline exceeded. The original validation result and usage are unchanged.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}}},"security":[{"platformKey":[]}],"summary":"Render a saved generation as a readable PDF","tags":["rendering"],"x-ironfang-scopes":["finance:einvoices:read"]}},"/v1/einvoices/reports":{"post":{"description":"Requires finance: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","operationId":"createEinvoiceReport","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportRequest"}}},"required":true},"responses":{"200":{"content":{"application/zip":{"schema":{"format":"binary","type":"string"}}},"description":"Signed Ironfang Finance report ZIP attachment; Cache-Control no-store.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"400":{"$ref":"#/components/responses/MalformedRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/InsufficientScope"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"$ref":"#/components/responses/ResultGone"},"415":{"$ref":"#/components/responses/UnsupportedContentType"},"422":{"content":{"application/problem+json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Problem"},{"properties":{"code":{"enum":["report_incomplete"]},"status":{"const":422}}}]}}},"description":"Retained findings are truncated; no complete signed report can be issued.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"429":{"$ref":"#/components/responses/ReadRateLimited"},"500":{"$ref":"#/components/responses/ReadInternalError"},"503":{"content":{"application/problem+json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Problem"},{"properties":{"code":{"enum":["report_unavailable"]},"status":{"const":503}}}]}}},"description":"Report dependency unavailable; the saved result and usage are unchanged.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}}},"security":[{"platformKey":[]}],"summary":"Download a signed report for a retained result","tags":["reports"],"x-ironfang-scopes":["finance:einvoices:read"]}},"/v1/einvoices/reports/keys":{"get":{"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","operationId":"getEinvoiceReportKeys","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportKeySet"}}},"description":"Public report trust metadata.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}}},"security":[],"summary":"Read Ironfang Finance report signing public keys","tags":["reports"]}},"/v1/einvoices/reports/verify":{"post":{"description":"Send the ZIP as application/zip. No API key or subscription is needed.\nIronfang Finance 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 `ironfang finance 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","operationId":"verifyEinvoiceReport","requestBody":{"content":{"application/zip":{"schema":{"format":"binary","type":"string"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportVerification"}}},"description":"Integrity verification result; Cache-Control no-store.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"400":{"$ref":"#/components/responses/MalformedRequest"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"415":{"$ref":"#/components/responses/UnsupportedContentType"},"429":{"$ref":"#/components/responses/ReadRateLimited"},"500":{"$ref":"#/components/responses/ReadInternalError"}},"security":[],"summary":"Verify report integrity without an account","tags":["reports"]}},"/v1/einvoices/results":{"get":{"description":"Returns a bounded page of result metadata, newest first by creation time and operation ID. Include cursor (empty on the first page) for stable timestamp/ID cursors; pass next_cursor as cursor for subsequent pages. Without cursor, next_cursor is a legacy operation ID to pass as before. Total counts all matching metadata, including expired/deleted records, independently of the page boundary. Names are hidden and cannot match search after expiry or deletion. Requires finance:einvoices:read.","operationId":"listValidationResults","parameters":[{"description":"Legacy last operation ID; cannot be combined with cursor. Unknown or foreign IDs return 404.","in":"query","name":"before","schema":{"$ref":"#/components/schemas/OpaqueID"}},{"allowEmptyValue":true,"description":"Include empty on the first page to opt into stable cursors, then pass next_cursor here.","in":"query","name":"cursor","schema":{"maxLength":256,"type":"string"}},{"in":"query","name":"limit","schema":{"default":20,"maximum":100,"minimum":1,"type":"integer"}},{"in":"query","name":"kind","schema":{"enum":["validate","generate"],"type":"string"}},{"in":"query","name":"outcome","schema":{"enum":["valid","invalid"],"type":"string"}},{"in":"query","name":"ruleset","schema":{"$ref":"#/components/schemas/RulesetID"}},{"description":"Case-insensitive literal substring of operation ID or an available document name. NUL is rejected.","in":"query","name":"q","schema":{"maxLength":255,"type":"string"}},{"description":"Exact tenant-owned operation ID for bounded metadata lookup. Unknown or foreign IDs return an empty list.","in":"query","name":"operation_id","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"examples":{"empty":{"externalValue":"https://api.ironfang.com/finance/examples/v1-retained-results-empty.json"}},"schema":{"$ref":"#/components/schemas/RetainedResultList"}}},"description":"Tenant-owned saved results. Cache-Control is no-store.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"400":{"$ref":"#/components/responses/MalformedRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/InsufficientScope"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/ReadRateLimited"},"500":{"$ref":"#/components/responses/ReadInternalError"}},"security":[{"platformKey":[]}],"summary":"List saved validation and generation results","tags":["results"]}},"/v1/einvoices/results/{id}":{"delete":{"description":"Requires finance: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.","operationId":"deleteValidationResult","parameters":[{"in":"path","name":"id","required":true,"schema":{"$ref":"#/components/schemas/OpaqueID"}}],"responses":{"204":{"description":"Result bytes and findings are deleted; operation and usage metadata remain.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"400":{"$ref":"#/components/responses/MalformedRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/InsufficientScope"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/ReadRateLimited"},"500":{"$ref":"#/components/responses/ReadInternalError"}},"security":[{"platformKey":[]}],"summary":"Delete saved result bytes and findings","tags":["results"]},"get":{"description":"Returns the exact original JSON while retained. Unknown or foreign IDs answer 404; expired/deleted results answer 410. Requires finance:einvoices:read.","operationId":"getValidationResult","parameters":[{"in":"path","name":"id","required":true,"schema":{"$ref":"#/components/schemas/OpaqueID"}}],"responses":{"200":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ValidationVerdict"},{"$ref":"#/components/schemas/GenerationResult"}]}}},"description":"Tenant-owned saved results. Cache-Control is no-store.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"400":{"$ref":"#/components/responses/MalformedRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/InsufficientScope"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"$ref":"#/components/responses/ResultGone"},"429":{"$ref":"#/components/responses/ReadRateLimited"},"500":{"$ref":"#/components/responses/ReadInternalError"}},"security":[{"platformKey":[]}],"summary":"Read a saved validation or generation result","tags":["results"]}},"/v1/einvoices/rulesets":{"get":{"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","operationId":"listEInvoiceRulesets","parameters":[{"description":"Restrict to one document type. Omit for both. A repeated or unknown query parameter is `400 malformed_request`.","in":"query","name":"document_type","required":false,"schema":{"$ref":"#/components/schemas/DocumentType"}},{"in":"query","name":"profile","required":false,"schema":{"$ref":"#/components/schemas/ProfileFamily"}},{"description":"Restrict to one lifecycle state.","in":"query","name":"state","required":false,"schema":{"$ref":"#/components/schemas/RulesetState"}}],"responses":{"200":{"content":{"application/json":{"examples":{"list":{"externalValue":"https://api.ironfang.com/finance/examples/v1-ruleset-list.json","summary":"Upcoming, current and past releases for invoices"}},"schema":{"$ref":"#/components/schemas/RulesetList"}}},"description":"The matching rulesets, newest effective date first.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"400":{"$ref":"#/components/responses/MalformedRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/InsufficientScope"},"429":{"$ref":"#/components/responses/ReadRateLimited"},"500":{"$ref":"#/components/responses/ReadInternalError"}},"security":[{},{"platformKey":[]}],"summary":"List rulesets available for selection and reproduction","tags":["rulesets"],"x-ironfang-scopes":["finance:einvoices:rulesets:read"]}},"/v1/einvoices/rulesets/{id}":{"get":{"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","operationId":"getEInvoiceRuleset","parameters":[{"in":"path","name":"id","required":true,"schema":{"$ref":"#/components/schemas/RulesetID"}}],"responses":{"200":{"content":{"application/json":{"examples":{"historical":{"externalValue":"https://api.ironfang.com/finance/examples/v1-ruleset-historical.json","summary":"An exact historical release, still reproducible"}},"schema":{"$ref":"#/components/schemas/Ruleset"}}},"description":"The ruleset.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/InsufficientScope"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"$ref":"#/components/responses/RulesetDisabled"},"429":{"$ref":"#/components/responses/ReadRateLimited"},"500":{"$ref":"#/components/responses/ReadInternalError"}},"security":[{},{"platformKey":[]}],"summary":"Fetch one ruleset by its immutable id","tags":["rulesets"],"x-ironfang-scopes":["finance:einvoices:rulesets:read"]}},"/v1/einvoices/usage":{"get":{"description":"Requires finance:billing:manage. The month's `finance.validation` and `finance.generation` meters as the organisation's billing account holds them - free allowance, used, reserved by unfinished work and the charge so far before tax - and Finance's own record of the same month beside them (`operations`), whose charged operations and usage events always agree. `metered` is false where unified billing is off: nothing is charged there and `meters` is empty. Work belongs to the month it was admitted in, whenever it completes.","operationId":"getEinvoiceUsage","parameters":[{"description":"A past or current UTC calendar month as YYYY-MM; the current month when omitted.","in":"query","name":"period","required":false,"schema":{"pattern":"^[0-9]{4}-(0[1-9]|1[0-2])$","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageReport"}}},"description":"Private usage metadata. Cache-Control: no-store."},"400":{"$ref":"#/components/responses/MalformedRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/InsufficientScope"},"429":{"$ref":"#/components/responses/ReadRateLimited"},"500":{"$ref":"#/components/responses/ReadInternalError"}},"security":[{"platformKey":[]}],"summary":"Read Ironfang Finance usage for a month","tags":["billing"],"x-ironfang-scopes":["finance:billing:manage"]}},"/v1/einvoices/validate":{"post":{"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 Ironfang Finance 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","operationId":"validateEInvoice","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"},{"description":"`latest` (default) or an exact ruleset id. Read for raw XML bodies only.","in":"query","name":"ruleset","required":false,"schema":{"$ref":"#/components/schemas/RulesetSelector"}},{"description":"Profile family to resolve `latest` within. Omit to detect it from the document. Read for raw XML bodies only.","in":"query","name":"profile","required":false,"schema":{"$ref":"#/components/schemas/ProfileFamily"}},{"description":"Expected document type, or `auto` (default) to detect it from the root element. A mismatch is `422 unsupported_document_type`. Read for raw XML bodies only.","in":"query","name":"document_type","required":false,"schema":{"$ref":"#/components/schemas/DocumentTypeSelector"}}],"requestBody":{"content":{"application/xml":{"schema":{"$ref":"#/components/schemas/XMLDocument"}},"multipart/form-data":{"encoding":{"document":{"contentType":"application/xml, text/xml"},"options":{"contentType":"application/json"}},"schema":{"additionalProperties":false,"properties":{"document":{"$ref":"#/components/schemas/XMLDocument"},"options":{"$ref":"#/components/schemas/ValidationOptions"}},"required":["document"],"type":"object"}},"text/xml":{"schema":{"$ref":"#/components/schemas/XMLDocument"}}},"required":true},"responses":{"200":{"content":{"application/json":{"examples":{"exact-historical":{"externalValue":"https://api.ironfang.com/finance/examples/v1-validation-result-exact-historical.json","summary":"Reproduction against an exact historical release"},"exact-scheduled":{"externalValue":"https://api.ironfang.com/finance/examples/v1-validation-result-exact-scheduled.json","summary":"Migration testing against an exact scheduled release"},"invalid-findings":{"externalValue":"https://api.ironfang.com/finance/examples/v1-validation-result-invalid.json","summary":"An invoice that fails Peppol rules, anonymous, profile detected"},"malformed-xml":{"externalValue":"https://api.ironfang.com/finance/examples/v1-validation-result-malformed-xml.json","summary":"Malformed XML against an exact ruleset - deeper layers skipped"},"valid":{"externalValue":"https://api.ironfang.com/finance/examples/v1-validation-result-valid.json","summary":"A conformant invoice, authenticated, latest within a named profile"}},"schema":{"$ref":"#/components/schemas/ValidationVerdict"}}},"description":"A verdict. `status` is `completed`, `outcome` is `valid` or `invalid`, and no layer is in `error`. A standards failure at any layer is this response, not a Problem.","headers":{"X-Ironfang-Request-ID":{"$ref":"#/components/headers/XIronfangRequestID"},"X-Request-ID":{"$ref":"#/components/headers/XRequestIDEcho"}}},"400":{"$ref":"#/components/responses/MalformedRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/ScopeOrEligibility"},"409":{"$ref":"#/components/responses/IdempotencyConflict"},"410":{"$ref":"#/components/responses/ValidationGone"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"415":{"$ref":"#/components/responses/UnsupportedContentType"},"422":{"$ref":"#/components/responses/UnsupportedSelection"},"429":{"$ref":"#/components/responses/ValidationRateLimited"},"500":{"$ref":"#/components/responses/ValidationInternalError"},"503":{"$ref":"#/components/responses/ValidatorUnavailable"},"504":{"$ref":"#/components/responses/ValidationTimeout"}},"security":[{},{"platformKey":[]}],"summary":"Validate one e-invoice XML document","tags":["validation"],"x-ironfang-scopes":["finance:einvoices:write"]}}},"security":[{},{"platformKey":[]}],"servers":[{"description":"Production. Paths below are relative to this product prefix.","url":"https://api.ironfang.com/finance"}],"tags":[{"description":"Ironfang Finance usage as the organisation's billing account meters it, beside Finance's own content-free record. Payment, limits and invoices belong to the account at /billing/v1.","name":"billing"},{"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 Ironfang-Event-Id, Ironfang-Timestamp and Ironfang-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/finance/{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 an Ironfang Finance 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.","name":"deliveries"},{"description":"Authenticated durable validation and generation jobs and ordered batches.","name":"jobs"},{"description":"PHIVE validation of UBL Invoice and CreditNote XML with structured findings.","name":"validation"},{"description":"Deterministic JSON-to-UBL generation, validated before success.","name":"generation"},{"description":"Signed validation observations and independent report integrity verification.","name":"reports"},{"description":"Safe readable PDFs of retained successfully validated generation results.","name":"rendering"},{"description":"Authenticated retained validation and generation results.","name":"results"},{"description":"The immutable ruleset registry validation results are pinned to.","name":"rulesets"}]}