{
    "openapi": "3.1.0",
    "info": {
        "title": "VAT API",
        "version": "1.0.0",
        "description": "Free EU VAT number validation. Every applicable source (VIES REST, VIES SOAP, the Belgian KBO registry and two fallback providers) is queried at the same time; the answer is returned as soon as one source is definitive, and the slower sources still complete in the background to improve the stored record."
    },
    "servers": [
        {
            "url": "https://vatapi.be"
        }
    ],
    "security": [
        {
            "bearerAuth": []
        }
    ],
    "paths": {
        "/api/v1/vat/{vatNumber}": {
            "get": {
                "operationId": "checkVatNumber",
                "summary": "Validate a VAT number",
                "description": "Accepts messy input such as `BE 0684.579.082`, `be0684579082` or `0684579082` with `country=BE`. `GR` is treated as `EL`. `GB` is not covered by VIES and returns 422.",
                "parameters": [
                    {
                        "name": "vatNumber",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "BE0684579082"
                    },
                    {
                        "name": "country",
                        "in": "query",
                        "required": false,
                        "description": "Two letter country code when the number has no prefix.",
                        "schema": {
                            "type": "string",
                            "minLength": 2,
                            "maxLength": 2
                        }
                    },
                    {
                        "name": "fresh",
                        "in": "query",
                        "required": false,
                        "description": "Set to 1 to bypass the 24 hours cache.",
                        "schema": {
                            "type": "boolean"
                        }
                    },
                    {
                        "name": "requester",
                        "in": "query",
                        "required": false,
                        "description": "Your own VAT number, passed to VIES to obtain a consultation number.",
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "The check result. `valid` is null when no source could answer and nothing is cached; a `Retry-After` header is then sent.",
                        "headers": {
                            "Retry-After": {
                                "description": "Seconds to wait before retrying when `valid` is null.",
                                "schema": {
                                    "type": "integer"
                                }
                            },
                            "RateLimit-Limit": {
                                "description": "Requests allowed in the window closest to running out (`RateLimit-Policy` lists all windows).",
                                "schema": {
                                    "type": "integer"
                                }
                            },
                            "RateLimit-Remaining": {
                                "schema": {
                                    "type": "integer"
                                }
                            },
                            "RateLimit-Reset": {
                                "description": "Seconds until that window resets.",
                                "schema": {
                                    "type": "integer"
                                }
                            },
                            "X-Request-Id": {
                                "description": "Identifier of this request; quote it when reporting a problem. You may send your own.",
                                "schema": {
                                    "type": "string"
                                }
                            },
                            "ETag": {
                                "description": "Weak validator of the answer. Send it back as If-None-Match to get 304 when nothing changed (the check still counts against your quota).",
                                "schema": {
                                    "type": "string"
                                }
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/VatCheck"
                                }
                            }
                        }
                    },
                    "304": {
                        "description": "The answer is unchanged since the ETag sent in If-None-Match."
                    },
                    "401": {
                        "description": "Missing or invalid API key.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "The input could not be understood, or the country is not covered.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limit reached (`rate_limited`), or too many live lookups at once with nothing stored to fall back on (`too_many_live_lookups`).",
                        "headers": {
                            "Retry-After": {
                                "schema": {
                                    "type": "integer"
                                }
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/status": {
            "get": {
                "operationId": "getStatus",
                "summary": "Source health and VIES member state availability",
                "security": [],
                "responses": {
                    "200": {
                        "description": "Current status.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Status"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/health": {
            "get": {
                "operationId": "getHealth",
                "summary": "Health for uptime monitors",
                "description": "Database, Redis, queue backlog, Horizon, age of the last KBO import and of the VIES status. `down` (HTTP 503) when the database or Redis fails, `degraded` (HTTP 200) when anything else needs attention.",
                "security": [],
                "responses": {
                    "200": {
                        "description": "ok or degraded.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Health"
                                }
                            }
                        }
                    },
                    "503": {
                        "description": "down.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Health"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/stats": {
            "get": {
                "operationId": "getStats",
                "summary": "Measured response times of the last days",
                "description": "Time to response of real checks (format-invalid answers and background revalidations excluded), split into live and cached answers, plus per-source latency and which source answered first. Recomputed at most every few minutes. `advertise` is false while the sample is too small to be meaningful.",
                "security": [],
                "responses": {
                    "200": {
                        "description": "Current statistics.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Stats"
                                }
                            }
                        }
                    }
                }
            }
        }
    },
    "components": {
        "securitySchemes": {
            "bearerAuth": {
                "type": "http",
                "scheme": "bearer"
            }
        },
        "schemas": {
            "Error": {
                "type": "object",
                "required": [
                    "error"
                ],
                "properties": {
                    "error": {
                        "type": "object",
                        "required": [
                            "code",
                            "message"
                        ],
                        "properties": {
                            "code": {
                                "type": "string"
                            },
                            "message": {
                                "type": "string"
                            }
                        }
                    }
                }
            },
            "VatCheck": {
                "type": "object",
                "required": [
                    "vat_number",
                    "country_code",
                    "number",
                    "valid",
                    "name",
                    "address",
                    "consultation_number",
                    "check_id",
                    "checked_at",
                    "served_from",
                    "stale",
                    "conflict",
                    "source",
                    "sources",
                    "registry"
                ],
                "properties": {
                    "vat_number": {
                        "type": "string",
                        "example": "BE0684579082"
                    },
                    "country_code": {
                        "type": "string",
                        "example": "BE"
                    },
                    "number": {
                        "type": "string",
                        "example": "0684579082"
                    },
                    "valid": {
                        "type": [
                            "boolean",
                            "null"
                        ],
                        "description": "null when no source could give a definitive answer."
                    },
                    "name": {
                        "type": [
                            "string",
                            "null"
                        ]
                    },
                    "address": {
                        "type": [
                            "string",
                            "null"
                        ]
                    },
                    "consultation_number": {
                        "type": [
                            "string",
                            "null"
                        ]
                    },
                    "check_id": {
                        "type": [
                            "integer",
                            "null"
                        ],
                        "description": "Identifier of this stored check."
                    },
                    "checked_at": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "date-time"
                    },
                    "served_from": {
                        "type": "string",
                        "enum": [
                            "live",
                            "cache",
                            "stale_cache",
                            "none"
                        ]
                    },
                    "stale": {
                        "type": "boolean",
                        "description": "true when the stored answer is older than 24 hours; it is refreshed in the background, check again later for the new answer."
                    },
                    "conflict": {
                        "type": "boolean",
                        "description": "true when sources that gave a definitive answer disagree; the highest ranked source (VIES REST = VIES SOAP > KBO > vatcomply > controleerbtwnummer) wins."
                    },
                    "source": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "enum": [
                            "vies_rest",
                            "vies_soap",
                            "kbo_ws",
                            "vatcomply",
                            "controleerbtwnummer",
                            "kbo_open_data",
                            null
                        ]
                    },
                    "sources": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/SourceEntry"
                        }
                    },
                    "registry": {
                        "oneOf": [
                            {
                                "type": "null"
                            },
                            {
                                "$ref": "#/components/schemas/Registry"
                            }
                        ]
                    },
                    "reason": {
                        "type": "string",
                        "description": "Present with value `format` when the number failed the local syntax check."
                    },
                    "error": {
                        "$ref": "#/components/schemas/Error/properties/error"
                    }
                }
            },
            "SourceEntry": {
                "type": "object",
                "required": [
                    "source",
                    "status"
                ],
                "properties": {
                    "source": {
                        "type": "string",
                        "enum": [
                            "vies_rest",
                            "vies_soap",
                            "kbo_ws",
                            "vatcomply",
                            "controleerbtwnummer",
                            "kbo_open_data"
                        ]
                    },
                    "status": {
                        "type": "string",
                        "enum": [
                            "ok",
                            "error",
                            "skipped",
                            "inconclusive",
                            "pending",
                            "not_checked"
                        ],
                        "description": "`pending`: the source was still running when the answer was sent; its result is stored afterwards. `not_checked`: no stored attempt (cached answers)."
                    },
                    "valid": {
                        "type": [
                            "boolean",
                            "null"
                        ],
                        "description": "Present when the source answered; always null for `kbo_open_data`, which never decides validity."
                    },
                    "error": {
                        "type": "string"
                    },
                    "reason": {
                        "type": "string"
                    },
                    "latency_ms": {
                        "type": [
                            "integer",
                            "null"
                        ]
                    },
                    "checked_at": {
                        "type": "string",
                        "format": "date-time"
                    },
                    "last_success_at": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "date-time"
                    },
                    "from_cache": {
                        "type": "boolean"
                    },
                    "found": {
                        "type": "boolean",
                        "description": "`kbo_open_data` only: the enterprise exists in the imported KBO dump."
                    },
                    "active": {
                        "type": [
                            "boolean",
                            "null"
                        ],
                        "description": "`kbo_open_data` only: the enterprise is active."
                    },
                    "name": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "`kbo_open_data` only."
                    },
                    "address": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "`kbo_open_data` only."
                    },
                    "data_date": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "date",
                        "description": "`kbo_open_data` only: date of the imported dump."
                    },
                    "started_at_ms": {
                        "type": "integer",
                        "description": "Offset from the start of the request when the source was called."
                    },
                    "finished_at_ms": {
                        "type": [
                            "integer",
                            "null"
                        ],
                        "description": "Offset when the source answered; null while pending."
                    }
                }
            },
            "Registry": {
                "type": "object",
                "description": "Belgian KBO/BCE registry data. Never sets `valid`.",
                "properties": {
                    "source": {
                        "type": "string",
                        "const": "kbo"
                    },
                    "via": {
                        "type": "string",
                        "enum": [
                            "open_data",
                            "public_search"
                        ]
                    },
                    "found": {
                        "type": "boolean"
                    },
                    "active": {
                        "type": [
                            "boolean",
                            "null"
                        ]
                    },
                    "name": {
                        "type": [
                            "string",
                            "null"
                        ]
                    },
                    "address": {
                        "type": [
                            "object",
                            "null"
                        ]
                    },
                    "data_date": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "date"
                    },
                    "attribution": {
                        "type": "string"
                    },
                    "notice": {
                        "type": "string"
                    }
                }
            },
            "Health": {
                "type": "object",
                "required": [
                    "status",
                    "checks",
                    "checked_at"
                ],
                "properties": {
                    "status": {
                        "type": "string",
                        "enum": [
                            "ok",
                            "degraded",
                            "down"
                        ]
                    },
                    "checks": {
                        "type": "object",
                        "additionalProperties": {
                            "type": "object",
                            "required": [
                                "ok"
                            ],
                            "properties": {
                                "ok": {
                                    "type": "boolean"
                                }
                            }
                        }
                    },
                    "checked_at": {
                        "type": "string",
                        "format": "date-time"
                    }
                }
            },
            "Timing": {
                "type": "object",
                "required": [
                    "sample",
                    "p50_ms",
                    "p95_ms",
                    "avg_ms",
                    "advertise"
                ],
                "properties": {
                    "sample": {
                        "type": "integer"
                    },
                    "p50_ms": {
                        "type": [
                            "integer",
                            "null"
                        ]
                    },
                    "p95_ms": {
                        "type": [
                            "integer",
                            "null"
                        ]
                    },
                    "avg_ms": {
                        "type": [
                            "integer",
                            "null"
                        ]
                    },
                    "advertise": {
                        "type": "boolean"
                    }
                }
            },
            "Stats": {
                "type": "object",
                "required": [
                    "window_days",
                    "min_sample",
                    "advertise",
                    "live",
                    "cached",
                    "sources",
                    "winners",
                    "generated_at"
                ],
                "properties": {
                    "window_days": {
                        "type": "integer",
                        "example": 7
                    },
                    "min_sample": {
                        "type": "integer"
                    },
                    "advertise": {
                        "type": "boolean"
                    },
                    "live": {
                        "$ref": "#/components/schemas/Timing"
                    },
                    "cached": {
                        "$ref": "#/components/schemas/Timing"
                    },
                    "sources": {
                        "type": "array",
                        "items": {
                            "type": "object",
                            "properties": {
                                "source": {
                                    "type": "string",
                                    "enum": [
                                        "vies_rest",
                                        "vies_soap",
                                        "kbo_ws",
                                        "vatcomply",
                                        "controleerbtwnummer",
                                        "kbo_open_data"
                                    ]
                                },
                                "label": {
                                    "type": "string"
                                },
                                "sample": {
                                    "type": "integer"
                                },
                                "p50_ms": {
                                    "type": [
                                        "integer",
                                        "null"
                                    ]
                                },
                                "p95_ms": {
                                    "type": [
                                        "integer",
                                        "null"
                                    ]
                                },
                                "avg_ms": {
                                    "type": [
                                        "integer",
                                        "null"
                                    ]
                                },
                                "error_rate": {
                                    "type": [
                                        "number",
                                        "null"
                                    ]
                                },
                                "advertise": {
                                    "type": "boolean"
                                }
                            }
                        }
                    },
                    "winners": {
                        "type": "array",
                        "items": {
                            "type": "object",
                            "properties": {
                                "source": {
                                    "type": "string"
                                },
                                "label": {
                                    "type": "string"
                                },
                                "count": {
                                    "type": "integer"
                                },
                                "share": {
                                    "type": "number"
                                }
                            }
                        }
                    },
                    "generated_at": {
                        "type": "string",
                        "format": "date-time"
                    }
                }
            },
            "Status": {
                "type": "object",
                "properties": {
                    "sources": {
                        "type": "array",
                        "items": {
                            "type": "object"
                        }
                    },
                    "vies": {
                        "type": "object"
                    }
                }
            }
        }
    }
}