openapi: 3.1.0
info:
  title: Irish eInvoice Readiness API
  version: 1.0.0
  description: |
    Independent, informational checks for UBL 2.1, CII D16B and the published canonical JSON model.
    Not Revenue, certification, or a legal-compliance determination. No API key is currently required.
    Invoice contents are processed in memory and discarded; metadata and findings are retained for 90 days by default.
    Public v1 endpoints allow cross-origin browser clients. Validation is limited to 60 requests per source per hour by default.
servers:
  - url: https://e-invoices.verens.com
paths:
  /api/v1/validate:
    options: {summary: CORS preflight, responses: {'204': {description: Preflight accepted}}}
    post:
      summary: Validate a UBL, CII, or canonical JSON invoice
      description: Subject to a configurable per-source hourly limit (default 60). A completed validation returns HTTP 200 even when rule errors are found.
      requestBody:
        required: true
        content:
          application/xml: {schema: {type: string}, example: '<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">...</Invoice>'}
          text/xml: {schema: {type: string}}
          application/json: {schema: {$ref: '#/components/schemas/CanonicalInvoice'}}
          multipart/form-data: {schema: {type: object, required: [invoice], properties: {invoice: {type: string, format: binary}}}}
      responses:
        '200': {description: Validation completed, content: {application/json: {schema: {$ref: '#/components/schemas/ValidationResponse'}}}}
        '400': {$ref: '#/components/responses/Error'}
        '413': {$ref: '#/components/responses/Error'}
        '415': {$ref: '#/components/responses/Error'}
        '422': {$ref: '#/components/responses/Error'}
        '429': {$ref: '#/components/responses/Error'}
        '500': {$ref: '#/components/responses/Error'}
        '503': {$ref: '#/components/responses/Error'}
  /api/v1/samples:
    get:
      summary: List fictitious validation samples
      description: Returns metadata only. Fetch a sample by ID to obtain its XML or JSON content.
      responses:
        '200': {description: Sample catalogue, content: {application/json: {schema: {$ref: '#/components/schemas/SampleList'}}}}
  /api/v1/samples/{id}:
    get:
      summary: Retrieve a fictitious XML or JSON sample
      parameters: [{name: id, in: path, required: true, schema: {type: string}, example: json-arithmetic}]
      responses:
        '200': {description: Sample and expected readiness status, content: {application/json: {schema: {$ref: '#/components/schemas/SampleResponse'}}}}
        '404': {$ref: '#/components/responses/Error'}
  /api/v1/results/{uuid}:
    get:
      summary: Retrieve a retained result
      parameters: [{name: uuid, in: path, required: true, schema: {type: string, format: uuid}}]
      responses: {'200': {description: Retained validation result, content: {application/json: {schema: {$ref: '#/components/schemas/ValidationResponse'}}}}, '404': {$ref: '#/components/responses/Error'}}
  /api/v1/phase1/requirements:
    get:
      summary: Published Phase 1 capability requirements and questions
      responses: {'200': {description: Source-traceable catalogue, content: {application/json: {schema: {type: object}}}}}
  /api/v1/phase1/assess:
    post:
      summary: Assess a business or software product against published Phase 1 capabilities
      description: Answers and reports are evaluated synchronously and never persisted. Pending Revenue specifications are reported separately.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [subject, answers]
              properties:
                subject: {enum: [business, software_product]}
                answers: {type: object, additionalProperties: {enum: [yes, partial, no, unknown, not_applicable]}}
      responses:
        '200': {description: Non-persistent assessment report, content: {application/json: {schema: {$ref: '#/components/schemas/Phase1Assessment'}}}}
        '413': {$ref: '#/components/responses/Error'}
        '415': {$ref: '#/components/responses/Error'}
        '422': {$ref: '#/components/responses/Error'}
  /api/v1/readiness/requirements:
    get:
      summary: Phase 1 and Phase 2 capability requirements
      description: Returns source-linked published requirements, existing cross-border VAT conditions, and specifications that remain pending.
      responses:
        '200': {description: Versioned readiness catalogue, content: {application/json: {schema: {$ref: '#/components/schemas/ReadinessCatalogue'}}}}
  /api/v1/readiness/assess:
    post:
      summary: Assess Phase 1, Phase 2, or combined readiness
      description: Answers and reports are evaluated synchronously and never persisted.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [subject, phase, answers]
              properties:
                subject: {enum: [business, software_product]}
                phase: {enum: [phase1, phase2, both]}
                answers: {type: object, additionalProperties: {enum: [yes, partial, no, unknown, not_applicable]}}
            example: {subject: business, phase: both, answers: {P1-SCOPE-01: yes, P2-SCOPE-01: yes, P2-SCOPE-02: partial}}
      responses:
        '200': {description: Non-persistent phase-aware assessment, content: {application/json: {schema: {$ref: '#/components/schemas/ReadinessAssessment'}}}}
        '413': {$ref: '#/components/responses/Error'}
        '415': {$ref: '#/components/responses/Error'}
        '422': {$ref: '#/components/responses/Error'}
  /api/v1/rulesets:
    get: {summary: List active versioned rulesets, responses: {'200': {description: Rulesets, content: {application/json: {schema: {type: object}}}}}}
  /api/v1/capabilities:
    get: {summary: List formats, limits, retention, and CORS policy, responses: {'200': {description: Capabilities, content: {application/json: {schema: {type: object}}}}}}
  /api/v1/health:
    get: {summary: Lightweight service health, responses: {'200': {description: Health, content: {application/json: {schema: {type: object}}}}, '503': {$ref: '#/components/responses/Error'}}
components:
  responses:
    Error: {description: API error, content: {application/json: {schema: {$ref: '#/components/schemas/Error'}}}}
  schemas:
    CanonicalInvoice:
      type: object
      required: [invoice, supplier, customer, lines, totals]
      properties:
        invoice: {type: object, properties: {number: {type: string}, issueDate: {type: string, format: date}, supplyDate: {type: string, format: date}, dueDate: {type: string, format: date}, currency: {type: string}, type: {type: string}}}
        supplier: {$ref: '#/components/schemas/Party'}
        customer: {$ref: '#/components/schemas/Party'}
        lines: {type: array, items: {type: object, properties: {id: {type: string}, description: {type: string}, quantity: {type: string}, unit: {type: string}, unitPrice: {type: string}, netAmount: {type: string}, vatCategory: {type: string}, vatRate: {type: string}}}}
        allowances: {type: array, items: {type: object}}
        charges: {type: array, items: {type: object}}
        vatBreakdown: {type: array, items: {type: object}}
        totals: {type: object}
        payment: {type: object}
        references: {type: object}
        taxTreatment: {type: object}
        notes: {type: array, items: {type: string}}
    Party: {type: object, properties: {name: {type: string}, address: {type: string}, country: {type: string}, vatNumber: {type: string}, electronicAddress: {type: string}}}
    Sample:
      type: object
      required: [id, name, syntax, description, expected_status, content_type]
      properties:
        id: {type: string, example: json-arithmetic}
        name: {type: string, example: 'JSON: incorrect line arithmetic'}
        syntax: {type: string, example: Canonical JSON}
        description: {type: string}
        expected_status: {type: string, example: needs_attention}
        content_type: {enum: [application/xml, application/json]}
        content: {type: string, description: Present only on the single-sample endpoint}
    SampleList:
      type: object
      required: [api_version, samples]
      properties:
        api_version: {const: '1'}
        samples: {type: array, items: {$ref: '#/components/schemas/Sample'}}
    SampleResponse:
      type: object
      required: [api_version, sample]
      properties:
        api_version: {const: '1'}
        sample: {$ref: '#/components/schemas/Sample'}
    ValidationResponse:
      type: object
      required: [validation_id, api_version, created_at, input, rulesets, summary, issues]
      properties:
        validation_id: {type: string, format: uuid}
        api_version: {const: '1'}
        created_at: {type: string, format: date-time}
        input: {type: object, required: [format, syntax, size]}
        rulesets: {type: array, items: {type: object, required: [name, version]}}
        summary: {type: object, required: [status, score, passed, warnings, errors, undetermined]}
        issues: {type: array, items: {$ref: '#/components/schemas/Issue'}}
        validation_profile: {type: object, description: Validation layers and authoritative artefact version applied}
        coverage: {type: object, description: Completed checks and specifications still pending publication}
    Issue: {type: object, required: [rule, ruleset, severity, title, message], properties: {rule: {type: string}, ruleset: {type: string}, severity: {enum: [error, warning, info, undetermined]}, title: {type: string}, message: {type: string}, field: {type: [string, 'null']}, source: {type: [string, 'null'], format: uri}}}
    Error: {type: object, required: [error], properties: {error: {type: object, required: [code, message, request_id], properties: {code: {type: string}, message: {type: string}, request_id: {type: string, format: uuid}}}}}
    Phase1Assessment:
      type: object
      required: [assessment_id, api_version, subject, scope, summary, findings, coverage, privacy]
      properties:
        assessment_id: {type: string, format: uuid}
        api_version: {const: '1'}
        subject: {enum: [business, software_product]}
        scope: {type: object}
        summary: {type: object}
        findings: {type: array, items: {type: object}}
        coverage: {type: object}
        privacy: {type: object, properties: {answers_persisted: {const: false}, result_persisted: {const: false}}}
    ReadinessCatalogue:
      type: object
      required: [api_version, catalogue_version, phases, answer_values, sources, requirements]
      properties:
        api_version: {const: '1'}
        catalogue_version: {type: string}
        phases: {type: array, items: {enum: [phase1, phase2]}}
        answer_values: {type: array, items: {type: string}}
        sources: {type: array, items: {type: object}}
        requirements: {type: array, items: {type: object, required: [code, phase, title, category, kind, subjects, status, source]}}
    ReadinessAssessment:
      type: object
      required: [assessment_id, api_version, subject, phase, rulesets, scopes, summary, findings, coverage, privacy]
      properties:
        assessment_id: {type: string, format: uuid}
        api_version: {const: '1'}
        subject: {enum: [business, software_product]}
        phase: {enum: [phase1, phase2, both]}
        rulesets: {type: array, items: {type: object}}
        scopes: {type: object}
        summary: {type: object}
        findings: {type: array, items: {type: object}}
        coverage: {type: object}
        privacy: {type: object, properties: {answers_persisted: {const: false}, result_persisted: {const: false}}}
