# Validation

URL: https://postman.slovakodata.com/developers/en/validacia/
Updated: 2026-09-02

Five validation layers from XML to Slovak rules.

Five layers. The first fatal error stops the rest and the document is not sent.

| Layer | What it checks | On failure |
|---|---|---|
| `L1` | Well-formed XML | <span class="status-pill s-danger">FATAL</span> |
| `L2` | UBL 2.1 schema (XSD) | <span class="status-pill s-danger">FATAL</span> |
| `L3` | EN 16931 (CEN Schematron) | <span class="status-pill s-danger">FATAL</span> or <span class="status-pill s-warn">WARNING</span> |
| `L4` | Peppol BIS Billing 3.0 (Schematron) | <span class="status-pill s-danger">FATAL</span> |
| `L5` | Slovak rules per the BIS transposition | <span class="status-pill s-danger">FATAL</span> or <span class="status-pill s-warn">WARNING</span> |

### Every layer reports its own result

Layers `L2`, `L3` and `L4` run as a single chain, so the **rules are never executed twice**.
Each layer still reports its own result — a finding is attributed to the rule set that
produced it.

A layer is <span class="status-pill s-ok">OK</span>,
<span class="status-pill s-warn">WARNING</span>, <span class="status-pill s-danger">FATAL</span>
or <span class="status-pill s-warn">SKIPPED</span>.

**`SKIPPED` does not mean "passed".** It means the layer never ran because the chain stopped
earlier: an XSD error stops both `L3` and `L4`, a fatal error at `L1` stops everything else.
"Not checked" and "fine" never collapse into the same value.

Layer `L5` can additionally return `ERROR` — the Slovak rule set failed to execute. This does
not stop the document, but a finding with `rule_id` `SK_ENGINE` is added to the warnings so the
response makes it obvious that the Slovak rules were not verified.

### What the Slovak layer checks

Layer `L5` runs when the supplier is a Slovak entity. It has two severities —
a fatal error stops the document, a warning only flags it and lets it through.

- **Fatal:** a missing `0245` scheme on identifiers, an incomplete supplier or buyer address.
- **Warning:** a suspicious tax ID or company ID format, a missing legal form, tax-representative inconsistencies.

### Validation result via the API

```http
GET /api/v1/invoices/{id}/validation
X-Api-Key: …
Accept-Language: en
```

Returns the **latest** verdict stored for the invoice. Results are append-only — revalidating
after a fix adds a new record and the previous one stays in history.

| Status | When |
|---|---|
| `200` | a verdict exists |
| `202` | the invoice is still `PENDING` or `VALIDATING`; both `retry_after` and the `Retry-After` header tell you how many seconds to wait |
| `404` | the invoice does not exist — or predates result persistence and will never get a verdict |

Findings have **the same shape as in [pre-flight validation](/developers/en/predbezna-validacia/)**:
`title`, `explanation` and `fix` are localized according to the `Accept-Language` header (`sk`
or `en`, `en` without the header; the language used comes back in `Content-Language`), while
`raw_message` and `raw_location` are the original Schematron text and XPath. One finding model
is therefore enough for both endpoints.

```json
{
  "id": "5a0c…",
  "invoice_id": "7104dd91-…",
  "created_at": "2026-09-01T09:41:10Z",
  "valid": false,
  "overall_severity": "FATAL",
  "validation_levels": {
    "L1_xml": "OK", "L2_xsd": "OK", "L3_en16931": "OK",
    "L4_peppol": "FATAL", "L5_sk": "SKIPPED"
  },
  "errors": [{
    "rule_id": "PEPPOL-EN16931-R001",
    "rule_set": "PEPPOL",
    "level": 4,
    "severity": "FATAL",
    "raw_message": "Business process MUST be provided.",
    "raw_location": "/Invoice[1]/cbc:ProfileID",
    "title": "Business process identifier is missing",
    "explanation": "Peppol requires the invoice to declare…",
    "fix": "Add the cbc:ProfileID element with the value…",
    "location": "/Invoice[1]/cbc:ProfileID",
    "localized": true
  }],
  "warnings": [],
  "truncated": false,
  "total_findings": 1,
  "duration_ms": 1840
}
```

The example is trimmed — the response also carries the deprecated fields described below.
`id` tells you which validation run produced the verdict.

If we do not have the rule in the catalogue yet, `localized` is `false` and `title` carries the
original English message. With more than 500 findings the list is truncated (`truncated`) and
the real count is in `total_findings`; errors take precedence over warnings.

#### Deprecated fields — removed in 2.0.0

Until version 2.0.0 the response also carries the original fields with identical values, so
existing integrations keep working unchanged. They are marked `deprecated: true` in OpenAPI.

| Deprecated | Use instead |
|---|---|
| `overallSeverity` | `overall_severity` |
| `validationLevels` | `validation_levels` |
| `durationMs` | `duration_ms` |
| `invoiceId` | `invoice_id` |
| `createdAt` | `created_at` |
| `organizationId` | no replacement — it is the caller's own organisation |
| finding: `ruleId` | `rule_id` |
| finding: `ruleSet` | `rule_set` |
| finding: `message` | `raw_message` (original) or `title` (localized) |
| finding: `location` | `raw_location` — until 2.0.0 `location` carries the XPath, afterwards it becomes the readable position as in pre-flight validation |
