{
  "openapi": "3.1.0",
  "info": {
    "title": "E-Rechnung API (XRechnung & ZUGFeRD Validator)",
    "version": "0.1.0-beta",
    "summary": "Validate German/EU e-invoices (XRechnung CII/UBL, ZUGFeRD/Factur-X PDF) via REST and get JSON with rule IDs.",
    "description": "REST API to validate e-invoices against the published KoSIT Schematron rules (EN 16931 and, when the document carries an XRechnung customization ID, XRechnung 3.0.2) for CII and UBL. ZUGFeRD/Factur-X PDFs are accepted; the first embedded XML file is checked (not PDF/A-3 or XMP metadata). There is **no XSD schema validation**, so results are not identical to the full KoSIT validator.\n\n**Auth:** API key (`erk_…`) as `Authorization: Bearer <key>` or `X-API-Key: <key>` (if a well-formed `Authorization: Bearer <key>` header is present it is used, otherwise `X-API-Key`). Get a free key instantly (email only, no card) at https://erechnung-konverter.vercel.app/e-rechnung-api or via `POST /api/v1/keys`. Use the key server-side only; the API sends no CORS headers.\n\n**Beta:** free, no availability guarantee; keys can be blocked in case of abuse.\n\n**Free tier (available now):** 50 validations per month per key (UTC calendar month), max 60 requests per minute per key. A validation is counted only when it actually runs (including `valid: false` results and 500 `internal_error`); requests rejected before validation (400, 401, 413, 415, 429, 503) are not counted.\n\n**Paid tiers:** planned, not yet available; see https://erechnung-konverter.vercel.app/e-rechnung-api#tarife.\n\nInvoice files are processed in memory only, never stored and never sent to AI services. Privacy: https://erechnung-konverter.vercel.app/datenschutz#api. Only `POST` is supported; `GET` on both endpoints returns 405 `method_not_allowed` with `Allow: POST`.\n\nKurz auf Deutsch: XRechnung (CII/UBL) und ZUGFeRD per POST prüfen, JSON mit Regel-ID, Originalmeldung und deutscher Kurzerklärung. Kostenlos 50 Prüfungen pro Monat; bezahlte Tarife geplant. Doku: https://erechnung-konverter.vercel.app/e-rechnung-api",
    "contact": {
      "name": "E-Rechnung Konverter",
      "email": "stoertebeker@mail.grokbot.com",
      "url": "https://erechnung-konverter.vercel.app/impressum"
    }
  },
  "externalDocs": {
    "description": "Documentation (German, with English summary)",
    "url": "https://erechnung-konverter.vercel.app/e-rechnung-api"
  },
  "servers": [
    {
      "url": "https://erechnung-konverter.vercel.app",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Validation",
      "description": "Validate XRechnung / ZUGFeRD e-invoices."
    },
    {
      "name": "Keys",
      "description": "Self-serve free API keys."
    }
  ],
  "paths": {
    "/api/v1/validate": {
      "post": {
        "operationId": "validateInvoice",
        "tags": [
          "Validation"
        ],
        "summary": "Validate an e-invoice (XRechnung CII/UBL or ZUGFeRD/Factur-X PDF)",
        "description": "Send the invoice as raw body (`application/xml`, `text/xml`, `application/…+xml` / `text/…+xml`, or `application/pdf`) or as multipart field `file` (XML or PDF). Max 2.5 MB per file; for PDFs the first embedded file is extracted (max 10 MB decoded). XML with a DOCTYPE is rejected. XML whose root is not a CII `CrossIndustryInvoice` or UBL `Invoice`/`CreditNote` returns 400 `not_an_einvoice` and does not count.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "apiKeyHeader": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/xml": {
              "schema": {
                "type": "string",
                "description": "XRechnung / EN 16931 invoice XML (CII or UBL)."
              }
            },
            "text/xml": {
              "schema": {
                "type": "string"
              }
            },
            "application/pdf": {
              "schema": {
                "type": "string",
                "format": "binary",
                "contentMediaType": "application/pdf",
                "description": "ZUGFeRD / Factur-X PDF with embedded invoice XML."
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "Invoice XML or ZUGFeRD/Factur-X PDF (max 2.5 MB)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Validation ran (the invoice may still be invalid, see `valid`). Counts as one validation.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Cache-Control": {
                "$ref": "#/components/headers/Cache-Control"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationResult"
                },
                "example": {
                  "valid": false,
                  "format": "CII",
                  "document": "invoice",
                  "profile": "urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0",
                  "rules": "KoSIT XRechnung 3.0.2 Schematron (CII: en16931 + xrechnung)",
                  "errors": [
                    {
                      "rule": "BR-DE-15",
                      "message": "Das Element \"Buyer reference\" (BT-10) muss übermittelt werden.",
                      "message_de": "Die Leitweg-ID bzw. Käuferreferenz (BT-10) fehlt."
                    }
                  ],
                  "warnings": [],
                  "invoice": {
                    "number": "123456XX",
                    "total": "336.9"
                  },
                  "usage": {
                    "plan": "free",
                    "limit": 50,
                    "used": 3,
                    "remaining": 47,
                    "reset": "2026-11-01T00:00:00.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Empty body, unreadable multipart body, missing multipart field `file`, unreadable PDF, PDF without embedded XML, embedded XML that could not be decoded, DOCTYPE in XML (`bad_request`), or XML that is not a CII/UBL e-invoice (`not_an_einvoice`). Not counted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "additionalProperties": false,
                      "properties": {
                        "code": {
                          "type": "string",
                          "enum": [
                            "bad_request",
                            "not_an_einvoice"
                          ]
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable message (German, often followed by an English translation after \" / \")."
                        }
                      }
                    }
                  },
                  "example": {
                    "error": {
                      "code": "not_an_einvoice",
                      "message": "Keine E-Rechnung erkannt: erwartet CrossIndustryInvoice (CII) oder UBL Invoice/CreditNote / not a CII or UBL e-invoice."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No key sent (`api_key_required`) or malformed/unknown key (`unauthorized`). Check order: missing/malformed key → 401 first; a well-formed but unknown key is only looked up after the body checks, so a bad body returns 400/413/415 instead. Not counted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "additionalProperties": false,
                      "properties": {
                        "code": {
                          "type": "string",
                          "enum": [
                            "api_key_required",
                            "unauthorized"
                          ]
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable message (German, often followed by an English translation after \" / \")."
                        }
                      }
                    }
                  },
                  "example": {
                    "error": {
                      "code": "api_key_required",
                      "message": "API-Schlüssel fehlt. Kostenlosen Schlüssel holen: https://erechnung-konverter.vercel.app/e-rechnung-api / API key required, get a free key at https://erechnung-konverter.vercel.app/e-rechnung-api"
                    }
                  }
                }
              }
            }
          },
          "413": {
            "description": "File over 2.5 MB, or embedded PDF XML over 10 MB decoded. Not counted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "additionalProperties": false,
                      "properties": {
                        "code": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable message (German, often followed by an English translation after \" / \")."
                        }
                      }
                    }
                  },
                  "example": {
                    "error": {
                      "code": "payload_too_large",
                      "message": "Max. 2,5 MB pro Datei / max 2.5 MB per file."
                    }
                  }
                }
              }
            }
          },
          "415": {
            "description": "Unsupported Content-Type. Not counted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "additionalProperties": false,
                      "properties": {
                        "code": {
                          "type": "string",
                          "enum": [
                            "unsupported_media_type"
                          ]
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable message (German, often followed by an English translation after \" / \")."
                        }
                      }
                    }
                  },
                  "example": {
                    "error": {
                      "code": "unsupported_media_type",
                      "message": "Content-Type application/xml, text/xml, application/pdf oder multipart/form-data (Feld file)."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Monthly quota used up (`quota_exceeded`, with `X-RateLimit-*` headers) or too many requests: more than 60/minute per key, more than 20 invalid keys/hour per IP, or concurrent-write contention (`rate_limited`). The per-IP invalid-key limit is checked before the key, so it can precede a 401. `Retry-After` is always set. Not counted.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "additionalProperties": false,
                      "properties": {
                        "code": {
                          "type": "string",
                          "enum": [
                            "quota_exceeded",
                            "rate_limited"
                          ]
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable message (German, often followed by an English translation after \" / \")."
                        }
                      }
                    }
                  },
                  "example": {
                    "error": {
                      "code": "quota_exceeded",
                      "message": "Freikontingent (50/Monat) aufgebraucht, Reset 2026-11-01T00:00:00.000Z. …"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Validation failed unexpectedly. Counts as one validation; please report it.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "additionalProperties": false,
                      "properties": {
                        "code": {
                          "type": "string",
                          "enum": [
                            "internal_error"
                          ]
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable message (German, often followed by an English translation after \" / \")."
                        }
                      }
                    }
                  },
                  "example": {
                    "error": {
                      "code": "internal_error",
                      "message": "Prüfung fehlgeschlagen / validation failed."
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "API temporarily disabled (`service_unavailable`) or key storage unavailable (`storage_unavailable`; checked right after the key format, before the body). Not counted.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "additionalProperties": false,
                      "properties": {
                        "code": {
                          "type": "string",
                          "enum": [
                            "service_unavailable",
                            "storage_unavailable"
                          ]
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable message (German, often followed by an English translation after \" / \")."
                        }
                      }
                    }
                  },
                  "example": {
                    "error": {
                      "code": "storage_unavailable",
                      "message": "Schlüsselspeicher nicht verfügbar / key storage unavailable."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/keys": {
      "post": {
        "operationId": "createApiKey",
        "tags": [
          "Keys"
        ],
        "summary": "Create a free API key (50 validations / month)",
        "description": "Returns the key once; only its SHA-256 hash is stored. One key per email address (409 if one exists; email us to reset it or to release an address registered without your knowledge). Limits: 3 keys per hour per IP.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "minLength": 6,
                    "maxLength": 254,
                    "description": "Contact address for the key (one key per address)."
                  },
                  "notify": {
                    "type": "boolean",
                    "default": false,
                    "description": "Only the JSON literal `true` opts in to one message when paid tiers launch (sent only after confirmation via a double opt-in link)."
                  },
                  "website": {
                    "type": "string",
                    "maxLength": 0,
                    "description": "Omit or leave empty (spam trap; any non-empty/truthy value is rejected with 400 `bad_request`)."
                  }
                }
              },
              "example": {
                "email": "dev@example.com",
                "notify": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Key created. Store it now; it cannot be shown again.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/Cache-Control"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKey"
                },
                "example": {
                  "key": "erk_EXAMPLE_not_a_real_key_00000000000000000000",
                  "prefix": "1a2b3c4d",
                  "plan": "free",
                  "limit": 50
                }
              }
            }
          },
          "400": {
            "description": "Unreadable JSON, not an object, spam-trap field filled (`bad_request`) or invalid email (`invalid_email`).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "additionalProperties": false,
                      "properties": {
                        "code": {
                          "type": "string",
                          "enum": [
                            "bad_request",
                            "invalid_email"
                          ]
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable message (German, often followed by an English translation after \" / \")."
                        }
                      }
                    }
                  },
                  "example": {
                    "error": {
                      "code": "invalid_email",
                      "message": "Bitte eine gültige E-Mail-Adresse angeben."
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "A key already exists for this email address.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "additionalProperties": false,
                      "properties": {
                        "code": {
                          "type": "string",
                          "enum": [
                            "email_exists"
                          ]
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable message (German, often followed by an English translation after \" / \")."
                        }
                      }
                    }
                  },
                  "example": {
                    "error": {
                      "code": "email_exists",
                      "message": "Für diese E-Mail-Adresse gibt es bereits einen Schlüssel. …"
                    }
                  }
                }
              }
            }
          },
          "413": {
            "description": "Request body over 4 KB.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "additionalProperties": false,
                      "properties": {
                        "code": {
                          "type": "string",
                          "enum": [
                            "payload_too_large"
                          ]
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable message (German, often followed by an English translation after \" / \")."
                        }
                      }
                    }
                  },
                  "example": {
                    "error": {
                      "code": "payload_too_large",
                      "message": "Anfrage zu groß."
                    }
                  }
                }
              }
            }
          },
          "415": {
            "description": "Content-Type is not application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "additionalProperties": false,
                      "properties": {
                        "code": {
                          "type": "string",
                          "enum": [
                            "unsupported_media_type"
                          ]
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable message (German, often followed by an English translation after \" / \")."
                        }
                      }
                    }
                  },
                  "example": {
                    "error": {
                      "code": "unsupported_media_type",
                      "message": "Content-Type application/json erwartet."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "More than 3 keys per hour from this IP, or the daily limit for new keys was reached.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "additionalProperties": false,
                      "properties": {
                        "code": {
                          "type": "string",
                          "enum": [
                            "rate_limited"
                          ]
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable message (German, often followed by an English translation after \" / \")."
                        }
                      }
                    }
                  },
                  "example": {
                    "error": {
                      "code": "rate_limited",
                      "message": "Zu viele Schlüssel von dieser Adresse. Bitte später erneut versuchen."
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "API temporarily disabled or key storage unavailable.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "additionalProperties": false,
                      "properties": {
                        "code": {
                          "type": "string",
                          "enum": [
                            "service_unavailable",
                            "storage_unavailable"
                          ]
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable message (German, often followed by an English translation after \" / \")."
                        }
                      }
                    }
                  },
                  "example": {
                    "error": {
                      "code": "storage_unavailable",
                      "message": "Schlüsselspeicher nicht verfügbar."
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "erk_ + 43 base64url characters",
        "description": "`Authorization: Bearer erk_…`"
      },
      "apiKeyHeader": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "Alternative to Bearer: `X-API-Key: erk_…`"
      }
    },
    "headers": {
      "X-RateLimit-Limit": {
        "description": "Monthly quota of the key (free tier: 50).",
        "schema": {
          "type": "integer"
        }
      },
      "X-RateLimit-Remaining": {
        "description": "Validations left this month (UTC).",
        "schema": {
          "type": "integer"
        }
      },
      "X-RateLimit-Reset": {
        "description": "Unix timestamp (seconds) when the monthly quota resets (start of next month, UTC).",
        "schema": {
          "type": "integer"
        }
      },
      "Retry-After": {
        "description": "Seconds to wait before retrying.",
        "schema": {
          "type": "integer"
        }
      },
      "Cache-Control": {
        "description": "Always `no-store`.",
        "schema": {
          "type": "string",
          "const": "no-store"
        }
      }
    },
    "schemas": {
      "Finding": {
        "type": "object",
        "required": [
          "rule",
          "message",
          "message_de"
        ],
        "additionalProperties": false,
        "properties": {
          "rule": {
            "type": [
              "string",
              "null"
            ],
            "description": "Rule ID such as `BR-DE-15` (explained at https://erechnung-konverter.vercel.app/fehlercodes), or null if the message has no rule ID.",
            "examples": [
              "BR-DE-15"
            ]
          },
          "message": {
            "type": "string",
            "description": "Original rule message."
          },
          "message_de": {
            "type": [
              "string",
              "null"
            ],
            "description": "Short German explanation, or null if none exists yet for this rule."
          }
        }
      },
      "Usage": {
        "type": "object",
        "required": [
          "plan",
          "limit",
          "used",
          "remaining",
          "reset"
        ],
        "additionalProperties": false,
        "properties": {
          "plan": {
            "type": "string",
            "description": "Currently always `free`.",
            "examples": [
              "free"
            ]
          },
          "limit": {
            "type": "integer",
            "examples": [
              50
            ]
          },
          "used": {
            "type": "integer",
            "description": "Validations used this month including this one."
          },
          "remaining": {
            "type": "integer"
          },
          "reset": {
            "type": "string",
            "format": "date-time",
            "description": "Start of next month (UTC)."
          }
        }
      },
      "ValidationResult": {
        "type": "object",
        "required": [
          "valid",
          "format",
          "document",
          "profile",
          "rules",
          "errors",
          "warnings",
          "invoice",
          "usage"
        ],
        "additionalProperties": false,
        "properties": {
          "valid": {
            "type": "boolean",
            "description": "true if no rule with error severity is violated (warnings are listed separately)."
          },
          "format": {
            "type": "string",
            "enum": [
              "CII",
              "UBL"
            ],
            "description": "Detected syntax."
          },
          "document": {
            "type": "string",
            "enum": [
              "invoice",
              "credit_note"
            ]
          },
          "profile": {
            "type": [
              "string",
              "null"
            ],
            "description": "Specification/customization ID found in the document (CII GuidelineSpecifiedDocumentContextParameter or UBL CustomizationID)."
          },
          "rules": {
            "type": [
              "string",
              "null"
            ],
            "description": "Rule sets that were applied.",
            "examples": [
              "KoSIT XRechnung 3.0.2 Schematron (CII: en16931 + xrechnung)"
            ]
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Finding"
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Finding"
            }
          },
          "invoice": {
            "type": "object",
            "required": [
              "number",
              "total"
            ],
            "additionalProperties": false,
            "properties": {
              "number": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Invoice number (BT-1)."
              },
              "total": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Grand total (CII GrandTotalAmount / UBL TaxInclusiveAmount) as the string written in the XML, not normalized."
              }
            }
          },
          "usage": {
            "$ref": "#/components/schemas/Usage"
          }
        }
      },
      "ApiKey": {
        "type": "object",
        "required": [
          "key",
          "prefix",
          "plan",
          "limit"
        ],
        "additionalProperties": false,
        "properties": {
          "key": {
            "type": "string",
            "pattern": "^erk_[A-Za-z0-9_-]{43}$",
            "description": "The API key. Shown only once."
          },
          "prefix": {
            "type": "string",
            "pattern": "^[0-9a-f]{8}$",
            "description": "Non-secret key identifier (first 8 hex chars of the key hash), useful in support requests."
          },
          "plan": {
            "type": "string",
            "examples": [
              "free"
            ]
          },
          "limit": {
            "type": "integer",
            "description": "Validations per month.",
            "examples": [
              50
            ]
          }
        }
      }
    }
  }
}
