Ironfang Finance E-Invoicing V2 Reports API

Signed reports of retained results, and their verification.

Operations 2

POST /v2/einvoices/reports Download a signed report of a retained result #
POST /v2/einvoices/reports/verify Verify a signed report of either version, without an account #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/ironfang:ironfang-finance-v2-reports-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

ironfang-finance-v2-reports-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Ironfang Finance E-Invoicing V2 Reports API
  version: 2.0.0
  summary: Validate XRechnung, ZUGFeRD / Factur-X and Peppol invoices, as XML or as a PDF with embedded XML.
  description: 'V2 validates an e-invoice against the exact, immutable ruleset of its

    family and reports precisely what was checked.'
  contact:
    name: Ironfang
    email: hello@ironfang.uk
    url: https://ironfang.com/finance
  termsOfService: https://ironfang.com/legal/terms
  license:
    name: Proprietary
    identifier: LicenseRef-Ironfang-Proprietary
servers:
- url: https://api.ironfang.com/finance
  description: Production. Paths below are relative to this product prefix.
tags:
- name: Reports
  description: Signed reports of retained results, and their verification.
paths:
  /v2/einvoices/reports:
    post:
      tags:
      - Reports
      operationId: createEInvoiceReportV2
      summary: Download a signed report of a retained result
      description: 'A ZIP binding the exact retained result, its complete findings and

        the facts it records to an Ed25519 signature by Ironfang Finance.

        The report is in its result''s version. A V2 validation, XML or

        hybrid PDF, gets the V2 manifest

        (`ironfang/finance/einvoice/report-manifest/v2`): the input and, for

        a PDF, its embedded XML by hash; the ruleset release with its

        family, variant, syntax, scope and manifest checksum (see the

        ruleset''s `manifest_sha256`); for

        a hybrid release the PDF engine, PDF/A profiles and binding table it

        pins; both engines by image digest; every check group and layer;

        what was not checked; and the release''s rule artefacts by hash. A

        result made through V1 gets exactly the bundle

        `POST /finance/v1/einvoices/reports` issues, V1 manifest and all.


        The ZIP holds `manifest.json` (canonical), `signature.json`,

        `result.json` (the retained bytes), `findings.json` and a readable

        `report.html` derived from the manifest; with RFC 3161 timestamps

        configured, `timestamp.tsq` and `timestamp.tsr` too. V2 reports

        sign under their own domain and envelope schema, so a V1 signature

        can never be read as V2 or the reverse. Both are signed by the keys

        at `GET /finance/v1/einvoices/reports/keys`.


        Send `operation_id` (and, for a V1 generation only, `include_pdf`)

        in a JSON body of at most 1024 bytes. A V2 result has no readable

        PDF: `include_pdf` on one is `503 report_unavailable`. Truncated

        findings are `422 report_incomplete`. Nothing is validated or

        charged again and nothing is stored; issuance time and signature may

        differ between downloads of one result, its facts never do. Expiry

        and deletion are checked again before the bundle is released.

        A signature does not certify compliance.'
      security:
      - platformKey: []
      x-ironfang-scopes:
      - finance:einvoices:read
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ReportRequest'
      responses:
        '200':
          description: The signed report ZIP as an attachment. Cache-Control is no-store.
          content:
            application/zip:
              schema:
                type: string
                format: binary
        '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':
          $ref: '#/components/responses/ReportIncomplete'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
        '503':
          $ref: '#/components/responses/ReportUnavailable'
  /v2/einvoices/reports/verify:
    post:
      tags:
      - Reports
      operationId: verifyEInvoiceReportV2
      summary: Verify a signed report of either version, without an account
      description: 'Send the ZIP as `application/zip`. Ironfang Finance reads which

        manifest it carries (V1''s or V2''s) and checks the canonical

        manifest, the domain-separated signature against its own published

        keys, every bound member''s hash and length, the readable page and,

        where the report requires one, its RFC 3161 timestamp. It performs

        no network access, no validation and no retention. HTTP 200 answers

        `verified` true or false; verified means the report is intact and

        issued by a trusted key, not that the invoice is valid: read

        `invoice_outcome`. `report_schema` names the manifest; `family` and

        `scope` are the release''s (a V1 report is Peppol BIS Billing 3 at

        XML scope). Unknown or revoked keys, tampering and unknown schemas

        fail. For a completely local check, `ironfang finance verify` does

        the same with keys you obtain independently.


        Bounded to 48 MiB compressed and expanded, nine members, two checks

        at a time per API process and 15 seconds. No query, compression or

        Idempotency-Key. Per-address anonymous limits apply.'
      security:
      - {}
      requestBody:
        required: true
        content:
          application/zip:
            schema:
              type: string
              format: binary
      responses:
        '200':
          description: The integrity check. Cache-Control is no-store.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReportVerification'
              examples:
                verified:
                  summary: A verified V2 report of a hybrid ZUGFeRD PDF
                  externalValue: https://api.ironfang.com/finance/v2/examples/v2-report-verification.json
                tampered:
                  summary: A report whose findings were changed after signing
                  externalValue: https://api.ironfang.com/finance/v2/examples/v2-report-verification-failed.json
        '400':
          $ref: '#/components/responses/MalformedRequest'
        '413':
          $ref: '#/components/responses/PayloadTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedContentType'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
components:
  schemas:
    BillingRefusal:
      type: object
      description: 'On a billing refusal only: the product and meter the unified billing account refused, and when the free allowance renews (the start of the next UTC month) where that is the reason.'
      additionalProperties: false
      properties:
        product:
          type: string
          enum:
          - finance
        meter:
          type: string
          enum:
          - finance.validation
          - finance.generation
        reset_at:
          type: string
          format: date-time
    Family:
      type: string
      description: 'An invoice family. `zugferd-facturx` is one family: ZUGFeRD and Factur-X are corresponding release names of the same standard.'
      enum:
      - peppol-bis-billing-3
      - xrechnung
      - zugferd-facturx
    RequestID:
      type: string
      pattern: ^[A-Za-z0-9_-]{8,64}$
    Declared:
      type: object
      description: 'What the document itself declares, copied from its source identifiers. A declaration, not a verdict: validation decides whether it is true.'
      additionalProperties: false
      required:
      - syntax
      properties:
        syntax:
          type: string
          description: The syntax the root element names.
          enum:
          - ubl
          - cii
          - other
        root:
          type: string
          description: The root element's local name.
          maxLength: 64
        specification_id:
          type: string
          description: UBL CustomizationID, or CII GuidelineSpecifiedDocumentContextParameter/ID.
          maxLength: 256
        business_process_id:
          type: string
          description: UBL ProfileID, or CII BusinessProcessSpecifiedDocumentContextParameter/ID.
          maxLength: 256
        type_code:
          type: string
          description: The invoice type code, which decides invoice or credit note in CII.
          maxLength: 8
        family:
          $ref: '#/components/schemas/Family'
        variant:
          $ref: '#/components/schemas/Variant'
        pdf:
          type: object
          description: For a PDF, the invoice declarations in its XMP metadata.
          additionalProperties: false
          properties:
            conformance_level:
              type: string
              maxLength: 32
            document_file_name:
              type: string
              maxLength: 128
            document_type:
              type: string
              maxLength: 32
            version:
              type: string
              maxLength: 16
            pdfa_part:
              type: string
              maxLength: 4
            pdfa_conformance:
              type: string
              maxLength: 4
    Variant:
      type: string
      description: 'A variant or profile within a family. XRechnung: `core`, `extension`, `cvd`. ZUGFeRD / Factur-X: `minimum`, `basic-wl`, `basic`, `en16931`, `extended`, `xrechnung` (the ZUGFeRD XRECHNUNG reference profile). Peppol BIS Billing 3 has none. Recognition is not support: the ruleset list is the only statement of what is validated.'
      enum:
      - core
      - extension
      - cvd
      - minimum
      - basic-wl
      - basic
      - en16931
      - extended
      - xrechnung
    OpaqueID:
      type: string
      pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$
    RulesetID:
      type: string
      pattern: ^fwrs_[a-z0-9_]{1,80}$
    ReportRequest:
      type: object
      additionalProperties: false
      required:
      - operation_id
      properties:
        operation_id:
          $ref: '#/components/schemas/OpaqueID'
        include_pdf:
          type: boolean
          description: A readable PDF of a retained V1 generation. A V2 result has none.
    ReportVerification:
      type: object
      additionalProperties: false
      description: 'The integrity check of a signed report of either version. A

        verified report passed every stage and says what it records; an

        unverified one gives at least one reason in `errors` and records

        nothing.

        '
      required:
      - schema
      - verified
      - signature
      - artifact_hashes
      - timestamp
      - errors
      - claim
      properties:
        schema:
          type: string
          const: ironfang/finance/einvoice/report-verification/v2
        verified:
          type: boolean
        report_schema:
          type: string
          enum:
          - financewolf/einvoice/report-manifest/v1
          - ironfang/finance/einvoice/report-manifest/v2
        signature:
          type: string
          enum:
          - not_verified
          - verified
          - invalid
          - untrusted
        artifact_hashes:
          type: string
          enum:
          - not_verified
          - verified
          - invalid
        timestamp:
          type: string
          enum:
          - not_present
          - not_verified
          - verified
          - invalid
        manifest_sha256:
          $ref: '#/components/schemas/SHA256'
        operation_id:
          $ref: '#/components/schemas/OpaqueID'
        invoice_outcome:
          type: string
          enum:
          - valid
          - invalid
        ruleset_id:
          $ref: '#/components/schemas/RulesetID'
        family:
          $ref: '#/components/schemas/Family'
        scope:
          $ref: '#/components/schemas/Scope'
        key_id:
          type: string
          pattern: ^fw_ed25519_[0-9a-f]{64}$
        errors:
          type: array
          maxItems: 8
          items:
            type: string
            maxLength: 256
        claim:
          type: string
          maxLength: 1024
    Problem:
      type: object
      description: 'An RFC 9457 problem. `code` is the stable identifier and fixes `status` and `type`. `detail` is safe to show: it never carries parser output, invoice content or stack traces.'
      additionalProperties: false
      required:
      - type
      - title
      - status
      - detail
      - code
      - request_id
      properties:
        type:
          type: string
          format: uri
          pattern: ^https://ironfang\.com/problems/[a-z_]+$
        title:
          type: string
          minLength: 1
          maxLength: 128
        status:
          type: integer
          minimum: 400
          maximum: 599
        detail:
          type: string
          minLength: 1
          maxLength: 512
        instance:
          type: string
          format: uri-reference
          maxLength: 256
        code:
          type: string
          enum:
          - malformed_request
          - unauthenticated
          - insufficient_scope
          - not_found
          - idempotency_conflict
          - ruleset_disabled
          - result_gone
          - payload_too_large
          - unsupported_content_type
          - unknown_ruleset
          - selection_conflict
          - family_undetected
          - family_mismatch
          - unsupported_variant
          - unsupported_document_type
          - scope_unavailable
          - no_embedded_invoice
          - ambiguous_embedded_invoice
          - encrypted_pdf
          - processing_limit
          - rate_limited
          - quota_exhausted
          - internal_error
          - validator_unavailable
          - validation_timeout
          - delivery_unavailable
          - jobs_unavailable
          - report_incomplete
          - report_unavailable
          - 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
        request_id:
          $ref: '#/components/schemas/RequestID'
        errors:
          type: array
          description: Field-level errors, on `malformed_request` only.
          minItems: 1
          maxItems: 50
          items:
            $ref: '#/components/schemas/FieldError'
        detected:
          $ref: '#/components/schemas/Declared'
          description: 'On `family_mismatch`, `family_undetected` and `unsupported_variant` only: what the document declares, so a client can point to the right tool. Identifiers only, never invoice content.'
        operation_id:
          $ref: '#/components/schemas/OpaqueID'
          description: On an indeterminate validation only, the operation that was opened.
        outcome:
          type: string
          const: indeterminate
        usage_charged:
          type: boolean
          const: false
        retry_after_seconds:
          type: integer
          minimum: 1
          maximum: 3600
        billing:
          $ref: '#/components/schemas/BillingRefusal'
      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: ruleset_disabled
        then:
          properties:
            status:
              const: 410
            type:
              const: https://ironfang.com/problems/ruleset_disabled
      - if:
          properties:
            code:
              const: result_gone
        then:
          properties:
            status:
              const: 410
            type:
              const: https://ironfang.com/problems/result_gone
      - 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: selection_conflict
        then:
          properties:
            status:
              const: 422
            type:
              const: https://ironfang.com/problems/selection_conflict
      - if:
          properties:
            code:
              const: family_undetected
        then:
          properties:
            status:
              const: 422
            type:
              const: https://ironfang.com/problems/family_undetected
      - if:
          properties:
            code:
              const: family_mismatch
        then:
          properties:
            status:
              const: 422
            type:
              const: https://ironfang.com/problems/family_mismatch
      - if:
          properties:
            code:
              const: unsupported_variant
        then:
          properties:
            status:
              const: 422
            type:
              const: https://ironfang.com/problems/unsupported_variant
      - 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: scope_unavailable
        then:
          properties:
            status:
              const: 422
            type:
              const: https://ironfang.com/problems/scope_unavailable
      - if:
          properties:
            code:
              const: no_embedded_invoice
        then:
          properties:
            status:
              const: 422
            type:
              const: https://ironfang.com/problems/no_embedded_invoice
      - if:
          properties:
            code:
              const: ambiguous_embedded_invoice
        then:
          properties:
            status:
              const: 422
            type:
              const: https://ironfang.com/problems/ambiguous_embedded_invoice
      - if:
          properties:
            code:
              const: encrypted_pdf
        then:
          properties:
            status:
              const: 422
            type:
              const: https://ironfang.com/problems/encrypted_pdf
      - if:
          properties:
            code:
              const: processing_limit
        then:
          properties:
            status:
              const: 422
            type:
              const: https://ironfang.com/problems/processing_limit
      - if:
          properties:
            code:
              const: rate_limited
        then:
          properties:
            status:
              const: 429
            type:
              const: https://ironfang.com/problems/rate_limited
      - if:
          properties:
            code:
              const: quota_exhausted
        then:
          properties:
            status:
              const: 429
            type:
              const: https://ironfang.com/problems/quota_exhausted
      - if:
          properties:
            code:
              const: internal_error
        then:
          properties:
            status:
              const: 500
            type:
              const: https://ironfang.com/problems/internal_error
      - if:
          properties:
            code:
              const: validator_unavailable
        then:
          properties:
            status:
              const: 503
            type:
              const: https://ironfang.com/problems/validator_unavailable
      - 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: report_incomplete
        then:
          properties:
            status:
              const: 422
            type:
              const: https://ironfang.com/problems/report_incomplete
      - if:
          properties:
            code:
              const: report_unavailable
        then:
          properties:
            status:
              const: 503
            type:
              const: https://ironfang.com/problems/report_unavailable
      - if:
          properties:
            code:
              const: validation_timeout
        then:
          properties:
            status:
              const: 504
            type:
              const: https://ironfang.com/problems/validation_timeout
      - 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
    FieldError:
      type: object
      additionalProperties: false
      required:
      - field
      - code
      - message
      properties:
        field:
          type: string
          minLength: 1
          maxLength: 64
        code:
          type: string
          enum:
          - unknown_field
          - repeated
          - invalid_value
          - missing
        message:
          type: string
          minLength: 1
          maxLength: 256
    Scope:
      type: string
      enum:
      - xml
      - hybrid_pdf
    SHA256:
      type: string
      pattern: ^[0-9a-f]{64}$
  responses:
    InternalError:
      description: Ironfang Finance failed. Not a verdict on anything.
      content:
        application/problem+json:
          schema:
            allOf:
            - $ref: '#/components/schemas/Problem'
            - properties:
                code:
                  const: internal_error
    UnsupportedContentType:
      description: The media type, encoding or content is not accepted.
      content:
        application/problem+json:
          schema:
            allOf:
            - $ref: '#/components/schemas/Problem'
            - properties:
                code:
                  const: unsupported_content_type
    NotFound:
      description: No such release or result is visible.
      content:
        application/problem+json:
          schema:
            allOf:
            - $ref: '#/components/schemas/Problem'
            - properties:
                code:
                  const: not_found
    ResultGone:
      description: The result expired or was erased. Its operation record remains.
      content:
        application/problem+json:
          schema:
            allOf:
            - $ref: '#/components/schemas/Problem'
            - properties:
                code:
                  const: result_gone
    RateLimited:
      description: Too many requests.
      headers:
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
      content:
        application/problem+json:
          schema:
            allOf:
            - $ref: '#/components/schemas/Problem'
            - properties:
                code:
                  const: rate_limited
    PayloadTooLarge:
      description: The body is over the limit for its media type.
      content:
        application/problem+json:
          schema:
            allOf:
            - $ref: '#/components/schemas/Problem'
            - properties:
                code:
                  const: payload_too_large
    ReportUnavailable:
      description: Signed reports are not configured, or a readable PDF was asked for a result that has none. The result is unchanged.
      content:
        application/problem+json:
          schema:
            allOf:
            - $ref: '#/components/schemas/Problem'
            - properties:
                code:
                  const: report_unavailable
    MalformedRequest:
      description: The request is malformed.
      content:
        application/problem+json:
          schema:
            allOf:
            - $ref: '#/components/schemas/Problem'
            - properties:
                code:
                  const: malformed_request
    ReportIncomplete:
      description: The retained findings are truncated; no complete signed report can be issued.
      content:
        application/problem+json:
          schema:
            allOf:
            - $ref: '#/components/schemas/Problem'
            - properties:
                code:
                  const: report_incomplete
    InsufficientScope:
      description: The key lacks the scope this operation needs.
      content:
        application/problem+json:
          schema:
            allOf:
            - $ref: '#/components/schemas/Problem'
            - properties:
                code:
                  const: insufficient_scope
    Unauthenticated:
      description: The credential is missing or not accepted.
      content:
        application/problem+json:
          schema:
            allOf:
            - $ref: '#/components/schemas/Problem'
            - properties:
                code:
                  const: unauthenticated
  headers:
    RetryAfter:
      description: Seconds to wait before retrying. Mirrors `retry_after_seconds`.
      schema:
        type: integer
        minimum: 1
  securitySchemes:
    platformKey:
      type: http
      scheme: bearer
      description: An Ironfang platform API key minted in the portal, carrying the scopes an operation lists.
externalDocs:
  description: Ironfang Finance documentation
  url: https://ironfang.com/docs/finance