Quick Start
Get compliance information for your organisation in under 2 minutes:
# 1. Check NIS2 applicability for a 120-employee energy company in Germany curl -X POST https://api.nis2europe.eu/v1/scoping \ -H "X-API-Key: nis2_test_client_XXXX" \ -H "Content-Type: application/json" \ -d '{"tootajad":120,"sektor":"energia","kaive_meur":25,"riik":"de","keel":"en"}' # 2. Get NIS2 → ISO 27001 framework mapping (lawyer_* and grc_platform roles) curl https://api.nis2europe.eu/v1/framework-mapping \ -H "X-API-Key: nis2_test_client_XXXX" # 3. Browse the service catalogue with prices (all roles) curl https://api.nis2europe.eu/v1/services \ -H "X-API-Key: nis2_test_client_XXXX"
grc_platform key. Some endpoints are role-restricted — e.g. POST /v1/incident-templates is available to all roles except grc_platform, and framework-mapping excludes client. Check the Auth column in the Endpoints table before calling; a disallowed role returns 403 FORBIDDEN_ROLE.import requests API_KEY = "nis2_test_client_XXXX" # replace with your key BASE = "https://api.nis2europe.eu" headers = {"X-API-Key": API_KEY} # 1. Scoping check resp = requests.post(f"{BASE}/v1/scoping", headers=headers, json={ "tootajad": 120, "sektor": "energia", "kaive_meur": 25, "riik": "de", "keel": "en" }) result = resp.json()["data"] print(result["klassifikatsioon"]) # "essential" | "important" | "valjas" (out of scope) # 2. Framework mapping mapping = requests.get(f"{BASE}/v1/framework-mapping", headers=headers).json() # 3. Order a compliance document package order = requests.post(f"{BASE}/v1/orders", headers=headers, json={ "teenus": "pakett", "riik": "de" }).json()["data"] print(order["makselink"]) # Revolut payment link
const API_KEY = 'nis2_test_client_XXXX'; const BASE = 'https://api.nis2europe.eu'; // 1. Scoping check const scope = await fetch(`${BASE}/v1/scoping`, { method: 'POST', headers: { 'X-API-Key': API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tootajad: 120, sektor: 'energia', kaive_meur: 25, riik: 'de', keel: 'en' }) }).then(r => r.json()); console.log(scope.data.klassifikatsioon); // 2. All framework mappings const maps = await fetch(`${BASE}/v1/framework-mapping`, { headers: { 'X-API-Key': API_KEY } }).then(r => r.json());
Authentication
All API requests require an API key passed in the X-API-Key header.
| Key prefix | Mode | Description |
|---|---|---|
nis2_live_* | Live | Production key — real document generation, real payments via Revolut. |
nis2_test_* | Sandbox | Test key — realistic mock responses, no billing, no documents generated. |
Get a key — self-service (immediate)
Sign up programmatically with POST /v1/signup (no key required). You get a working sandbox key immediately in the response, plus a Revolut payment link for your chosen licence; the live key is emailed the moment payment is confirmed. The same signup is also available as a form on each country's API page.
curl -X POST https://api.nis2europe.eu/v1/signup \ -H "Content-Type: application/json" \ -d '{"nimi":"Example GmbH","email":"dev@example.com","roll":"grc_platform","riik":"de","litsents":"enterprise"}' # Response { "data": { "staatus": "ootel_makset", // live key pending payment "litsents": "enterprise", "sandbox_voti": "nis2_test_grc_platform_…", // works immediately (mock mode) — shown ONCE "order_id": "a8396e58-…", "makselink": "https://checkout.revolut.com/…", // pay to activate the live key "hind_eur": 9900, "dokumentatsioon": "https://api.nis2europe.eu/developers/", "note": "Sandbox key works immediately (mock mode). The live API key is generated and emailed after payment is received." }, "meta": { "powered_by": "AIPOS OÜ (reg. 16966532)", "request_id": "…" } }
| Field | Required | Notes |
|---|---|---|
nimi | yes | Company or individual name (2–200 chars) |
email | yes | Where the live key is sent after payment |
roll | yes | lawyer_firm / lawyer_individual / client / grc_platform |
litsents | no | professional (default) or enterprise |
riik | no | Billing country, ISO alpha-2 (drives VAT) |
kmkr | no | VAT number (reverse charge — see Pricing) |
Prefer email?
Alternatively, contact info@nis2europe.eu with your company name, role, and intended use case — we will issue a key manually. Self-service above is faster; the email route is there if you prefer a human.
Response envelope
Every response is wrapped in a consistent envelope:
{
"data": { ... }, // endpoint-specific payload
"meta": {
"powered_by": "AIPOS OÜ (reg. 16966532)",
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"environment": "sandbox" // only for nis2_test_ keys
}
}
Sandbox Mode
nis2_test_) give you full API access without any billing or real document generation. Responses carry meta.environment: "sandbox".
- All read endpoints (scoping, mapping, incident-templates, wizard) return real data from the knowledge base.
POST /v1/ordersreturns a single canonical mock order (order_id: "sandbox-demo-order-001") — no real Revolut payment link is created, no documents are generated. The mock is stateless, so bothPOSTandGETreturn byte-identical data for the demo order.GET /v1/orders/{order_id}returns the mock only for the demo idsandbox-demo-order-001; any other id returns404 NOT_FOUND— so you can exercise your not-found handling in sandbox exactly as in production.- The mock's
tulemus.dokumendid[].allalaadimislinkreturns a real watermarked SANDBOX SAMPLE PDF (HTTP 200,application/pdf) so you can test download handling end to end. - Sandbox keys auto-receive Professional-tier rate limits (300 req/min, 50 000 req/month).
- Sandbox environments are clearly marked in all API responses and must not be used in production.
Sandbox order response
{
"data": {
"order_id": "sandbox-demo-order-001",
"teenus": "pakett", "riik": "EU", // fixed demo order (not country-specific)
"makse_staatus": "tasutud",
"dokumendi_staatus": "vabastatud",
"tulemus": {
"dokumendid": [{ "nimi": "NIS2_Riskijuhtimispoliitika_SANDBOX.md",
"allalaadimislink": "https://api.nis2europe.eu/v1/download/sandbox-token-demo", ... }],
"sandbox_note": "SANDBOX: fixed demo order … live key runs a real, country-specific order."
}
},
"meta": { "environment": "sandbox", "powered_by": "AIPOS OÜ (reg. 16966532)", ... }
}
API Endpoints
Base URL: https://api.nis2europe.eu | Current version: v1
| Method | Path | Description | Auth |
|---|---|---|---|
| POST | /v1/signup | Self-service signup (sandbox key + payment link) | None |
| POST | /v1/scoping | NIS2 applicability check | All roles |
| GET | /v1/scoping/questions | Scoping question keys | All roles |
| GET | /v1/framework-mapping | All NIS2 art 21(2) ↔ ISO/DORA/GDPR | lawyer_*, grc_platform |
| GET | /v1/framework-mapping/{key} | Single measure mapping | lawyer_*, grc_platform |
| POST | /v1/incident-templates | 24h/72h/30d notification templates | All except grc_platform |
| GET | /v1/wizard/questions | Gap-analysis questions (localised) | All roles |
| POST | /v1/wizard/evaluate | Gap-analysis evaluation | All roles |
| GET | /v1/services | Service catalogue with prices | All roles |
| POST | /v1/orders | Create order + payment link | All roles |
| GET | /v1/orders/{order_id} | Order status + download links | All roles |
| GET | /v1/download/{token} | Download document (token-based, no auth) | Token |
| POST | /v1/account/vat | Set billing/VAT details | All roles |
| GET | /v1/billing/summary | Invoices, subscription, usage | All roles |
| GET | /v1/health | API health check (no auth) | None |
| GET | /v1/status | Full service health (no auth) | None |
| GET | /v1/changelog | KB version changelog (no auth) | None |
keel (query or body) on GET/most endpoints, language in the incident-templates body. This includes GET /v1/scoping/questions inputs, GET /v1/wizard/questions, POST /v1/wizard/evaluate, POST /v1/scoping, POST /v1/incident-templates, and GET /v1/services (e.g. GET /v1/services?keel=en returns English service names). Default language is Estonian unless a translation for the requested language is missing, in which case the field falls back to the default.POST /v1/scoping — NIS2 Applicability
Deterministic classification: essential / important / valjas (out of scope). No LLM, <200ms.
Request
{
"tootajad": 120, // employees (required); size class is derived from this + turnover
"sektor": "energia", // NIS2 Annex I/II sector key — full list: GET /v1/scoping/questions
"kaive_meur": 25, // annual turnover, MEUR (optional)
"riik": "de", // ISO 3166-1 alpha-2 (23 live EU countries)
"keel": "en" // response language ("en" | "et" | "de" | "fi" | ...)
}
Response
{
"data": {
"klassifikatsioon": "important", // "essential" | "important" | "valjas" (out of scope)
"suurus": "keskmine",
"sektor": "energia",
"annex": "I",
"pohjendused": ["A medium-sized entity in the sector 'Energy' (Annex I) -> IMPORTANT (Art 3(2))."],
"sources": [
{ "seadus": "Directive (EU) 2022/2555", "paragrahv": "art 3(1)", "kb_versioon": "kb_2026-07-22_c109ef7f" }
]
}
}
GET /v1/framework-mapping — Framework Cross-Mapping
NIS2 art 21(2) sub-measures (a–j) mapped to ISO 27001, DORA, and GDPR controls. Each control reference is an object {"viide", "nimetus"} (reference code + short name), not a bare string. The nimetus field of a measure is a per-language dictionary (22 languages).
Single measure — GET /v1/framework-mapping/{key}
{key} may be a letter (a–j), an article form (art 21(2)(a), (a), 21(2)(a)), or a name fragment. Unknown key → 404 NOT_FOUND.
curl https://api.nis2europe.eu/v1/framework-mapping/a \ -H "X-API-Key: nis2_test_client_XXXX" # Response (nimetus + control lists truncated for readability) { "data": { "voti": "a", "artikkel": "art 21(2)(a)", "nimetus": { // per-language dict (et, en, de, fi, … 22 langs) "et": "riskianalüüsi ja infosüsteemide turvalisuse poliitikad", "en": "policies on risk analysis and information system security" }, "kaardistus": { // arrays of {viide, nimetus} objects — NOT bare strings "iso27001": [ { "viide": "klausel 5.2", "nimetus": "infoturbepoliitika" }, { "viide": "klausel 6.1.2", "nimetus": "infoturberiski hindamine" } ], "dora": [ { "viide": "art 5", "nimetus": "juhtimine ja korraldus" } ], "gdpr": [ { "viide": "art 24", "nimetus": "vastutava töötleja kohustused (riskipõhine)" } ] }, "iso_sertifitseeritud": { // what an ISO 27001-certified org already has vs the NIS2 gap "kate": "tugev", // "tugev" (strong) | "osaline" (partial) "olemas": "ISMS-i riskihindamise metoodika, riskiregister, …", "vahe": "Kontrolli, et poliitika kohaldamisala kataks KÕIK NIS2 …" }, "sources": [ { "seadus": "direktiiv (EL) 2022/2555", "paragrahv": "art 21(2)(a)", "url": "https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:32022L2555", "seisukuupaev": "2023-01-16", "kb_versioon": "kb_2026-07-24_c109ef7f" } ] }, "meta": { "powered_by": "AIPOS OÜ (reg. 16966532)", "request_id": "…" } }
kirjeldus field, and iso27001/dora/gdpr live under kaardistus as arrays of objects. To read a control code use data.kaardistus.iso27001[0].viide; the human-readable measure name is data.nimetus["en"].All measures — GET /v1/framework-mapping
Returns every NIS2 art 21(2) sub-measure plus a certification baseline summary. Each item in meetmed[] has the exact same shape as the single-measure response above.
curl https://api.nis2europe.eu/v1/framework-mapping \ -H "X-API-Key: nis2_test_client_XXXX" # Response (truncated) { "data": { "baseline": { "kokku": 10, // total measures a–j "tugev_kate": ["art 21(2)(a)", "art 21(2)(c)", "…"], // strongly covered by ISO 27001 "osaline_kate": ["art 21(2)(b)", "art 21(2)(g)", "…"], // partially covered "vahed": { "art 21(2)(a)": "Kontrolli, et poliitika …" } // per-measure NIS2 gap }, "meetmed": [ { "voti": "a", "artikkel": "art 21(2)(a)", "nimetus": { "…" }, "kaardistus": { "…" }, "iso_sertifitseeritud": { "…" }, "sources": [ "…" ] }, { "voti": "b", "…" } // … 10 measures total (a–j) ] }, "meta": { "powered_by": "AIPOS OÜ (reg. 16966532)", "request_id": "…" } }
POST /v1/incident-templates — Notification Templates
Generate NIS2 art 23 incident notification text for all three stages (24h early warning, 72h incident notification, 30-day final report).
Request
{
"detected_at": "2026-07-22T10:00:00Z", // ISO 8601
"country": "de",
"language": "en",
"incident_summary": "Ransomware encrypting ERP system"
}
Response (truncated)
{
"data": {
"phases": [
{
"phase": "early_warning",
"deadline": "2026-07-23T10:00:00Z",
"legal_basis": "Directive (EU) 2022/2555 art 23(1)",
"template": "Early warning — 24 hours\n\n...",
"sources": [ ... ]
},
{ "phase": "incident_notification", ... },
{ "phase": "final_report", ... }
]
}
}
POST /v1/wizard/evaluate — Gap Analysis
Free gap analysis against all 10 NIS2 art 21(2) security measures. Returns current compliance score and prioritised recommendations. All 20 measure keys (a1–j2, retrievable from GET /v1/wizard/questions) are required; each answer is one of jah | osaliselt | ei (yes | partial | no). The response carries koondskoor out of max_skoor (40) plus a prioritised pingerida.
curl -X POST https://api.nis2europe.eu/v1/wizard/evaluate \ -H "X-API-Key: nis2_test_client_XXXX" \ -H "Content-Type: application/json" \ -d '{"vastused":{"a1":"jah","a2":"osaliselt","b1":"jah","b2":"ei","c1":"jah","c2":"osaliselt","d1":"jah","d2":"ei","e1":"jah","e2":"osaliselt","f1":"jah","f2":"ei","g1":"jah","g2":"osaliselt","h1":"jah","h2":"ei","i1":"jah","i2":"osaliselt","j1":"jah","j2":"ei"},"keel":"en","riik":"de"}'
# All 20 keys a1..j2 required; each value is "jah" | "osaliselt" | "ei" vastused = {k: "osaliselt" for k in [ "a1","a2","b1","b2","c1","c2","d1","d2","e1","e2", "f1","f2","g1","g2","h1","h2","i1","i2","j1","j2"]} resp = requests.post(f"{BASE}/v1/wizard/evaluate", headers=headers, json={ "vastused": vastused, "keel": "en", "riik": "de" }) data = resp.json()["data"] print(data["koondskoor"], "/", data["max_skoor"], data["jargmine_samm"])
Orders — Document Packages
Order a legally-grounded NIS2 compliance document package. Payment via Revolut; documents released within 15 minutes of payment confirmation.
POST /v1/orders — create order
{
"teenus": "pakett", // "pakett" | "b_skann" | "yksikdokument" | "hooldus" | ...
"riik": "de", // target jurisdiction
"idempotency_key": "your-unique-key-001" // optional; prevents duplicates
}
Response includes makselink (Revolut hosted payment page). After payment, documents are generated and available via the tulemus.dokumendid[].allalaadimislink fields. Download links are time-limited (72 hours).
GET /v1/orders/{order_id} — poll status
| makse_staatus | dokumendi_staatus | Meaning |
|---|---|---|
ootel | ootel | Waiting for payment |
tasutud | genereerimisel | Paid; generating documents |
tasutud | vabastatud | Documents ready; download links active |
POST /v1/account/vat — VAT / Billing Details
Set your VAT number and billing address. VIES validation runs at invoice creation time.
{
"kmkr": "DE123456789", // VAT registration number
"arveldus_riik": "de", // billing country (ISO alpha-2)
"arveldus_aadress": "Musterstraße 1, 10115 Berlin",
"reg_kood": "HRB 12345" // company register code
}
GET /v1/billing/summary — Usage & Invoices
Returns your orders, invoices (with download links), active subscription, and monthly API usage vs limit.
GET /v1/status — Service Health
Public endpoint (no auth). Returns overall health and per-service status.
{
"data": {
"overall": "operational",
"timestamp": "2026-07-22T18:00:00Z",
"teenused": {
"api": { "status": "ok" },
"database": { "status": "ok" },
"redis": { "status": "ok" },
"worker": { "status": "ok" }
}
}
}
GET /v1/changelog — KB Version History
Returns the structured knowledge base changelog — which legal source changed and when.
curl https://api.nis2europe.eu/v1/changelog
Error Codes
All errors follow the envelope format: {"error": {"code": "...", "message": "...", "request_id": "...", "details": [ ... ]}}
error.message is human-readable and defaults to English; it is returned in Estonian when the request sends ?keel=et or an Accept-Language: et header. error.code is a stable machine identifier and never changes with language — always branch on code, not message.VALIDATION_FAILED) include a structured error.details[] array — e.g. [{"field":"tootajad","type":"missing","msg":"Field required"}] — instead of a stringified Python object, so you can map errors to form fields programmatically. Other errors may also include details[] (e.g. the allowed roles for FORBIDDEN_ROLE).| HTTP | code | When |
|---|---|---|
| 401 | UNAUTHORIZED | Missing, unknown, or blocked API key |
| 403 | FORBIDDEN_ROLE | Your role is not permitted for this endpoint |
| 404 | NOT_FOUND | Resource does not exist (order, file, measure key) |
| 409 | IDEMPOTENCY_CONFLICT | Duplicate idempotency_key with different payload |
| 422 | VALIDATION_FAILED | Invalid request body or parameters |
| 429 | RATE_LIMITED | Too many requests; check Retry-After header |
| 500 | INTERNAL_ERROR | Unexpected server error; contact support |
| 503 | UPSTREAM_UNAVAILABLE | Dependent service temporarily unavailable |
On rate limiting, the response includes headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, Retry-After.
Rate Limits
| Tier | Req/min | Req/month |
|---|---|---|
| Baas (no subscription) | 60 | 2 000 |
| Professional (3 900 EUR/mo) | 300 | 50 000 |
| Enterprise (9 900 EUR/mo) | 1 200 | 500 000 |
Sandbox (nis2_test_*) | 300 | 50 000 |
Every response includes X-RateLimit-* headers so you can track consumption programmatically. Each authenticated API key is tracked independently.
Pricing
| Service | Price (EUR net) |
|---|---|
| Compliance Document Package | 8 900 – 17 900 (R1–R4) |
| Annual Maintenance | 2 900/month or 19 000/year prepaid |
| B-Scan (gap assessment) | 4 900 – 9 900 (R1–R4) |
| Framework mapping / single document / incident | 1 490 |
| Supply-chain attestation | 2 900 – 5 900 (R1–R4) |
| Re-sign (document update) | 1 900 – 3 900 (R1–R4) |
| Combo discount | max 10 % |
| API licence Professional | 3 900 / month |
| API licence Enterprise | 9 900 / month |
What the licence includes — and what it does not
| Without licence | Professional | Enterprise | |
|---|---|---|---|
| API access: scoping, gap analysis, incident templates, framework-mapping queries (rate limit + monthly request quota) | — | Included | Included — higher quota, SLA, priority queue |
| Documents at 1 490 (framework-mapping document, incident-notification template pack, single document) | 1 490 each | 745 each for up to 10 documents/month; thereafter 1 490 each | 745 each for up to 30 documents/month; thereafter 1 490 each |
| Larger products (document package, B-scan, supply-chain attestation, re-sign, maintenance) | Always full price — regardless of licence | ||
Payment rule: All orders are prepaid via Revolut to AIPOS OÜ. Documents are released only after payment confirmation. No credit terms.