NIS2Europe API Developer Portal

NIS2 Compliance API

Programmatic access to EU NIS2 compliance — scoping, gap analysis, incident templates, document packages — live for 24 EU countries in 21 languages. ES, FR and IE are added the moment their national transposition acts enter into force: we never issue documents based on law that is not yet in force.

Base URL: https://api.nis2europe.eu

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"

# 4. Generate incident-notification templates (24h/72h/30d)
#    Roles: lawyer_firm, lawyer_individual, client — i.e. ALL EXCEPT grc_platform.
#    A grc_platform key returns 403 FORBIDDEN_ROLE here — see the note below.
curl -X POST https://api.nis2europe.eu/v1/incident-templates \
  -H "X-API-Key: nis2_test_client_XXXX" \
  -H "Content-Type: application/json" \
  -d '{"detected_at":"2026-07-22T10:00:00Z","country":"de","language":"en","incident_summary":"Ransomware attack on core IT systems"}'
Endpoint roles differ — read before running example 4. Examples 1–3 work for every role, including a grc_platform key. Example 4 (POST /v1/incident-templates) is available to lawyer_firm, lawyer_individual and clientall roles except grc_platform — because it emits a ready-to-file incident notice for an end organisation, which is the compliance-service role rather than the platform role. A grc_platform key returns 403 FORBIDDEN_ROLE.
What a grc_platform integration uses instead: build the incident notification inside your own product from the two endpoints your key can call — GET /v1/framework-mapping (the NIS2 art 21(2) ↔ ISO/DORA/GDPR measure map, with source references) and POST /v1/scoping (the organisation's classification and applicable obligations) — so your users generate incident text within your own workflow. Always check the Auth column in the Endpoints table; 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 prefixModeDescription
nis2_live_*LiveProduction key — real document generation, real payments via Revolut.
nis2_test_*SandboxTest 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": 2900,
    "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": "…" }
}
FieldRequiredNotes
nimiyesCompany or individual name (2–200 chars)
emailyesWhere the live key is sent after payment
rollyeslawyer_firm / lawyer_individual / client / grc_platform
litsentsnoprofessional (default) or enterprise
riiknoBilling country, ISO alpha-2 (drives VAT)
kmkrnoVAT number (reverse charge — see Pricing)
Signup is rate-limited to 5 requests/hour per IP. The sandbox key is enough to integrate and test every endpoint end-to-end before you pay.

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

Sandbox keys (prefix 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/orders returns 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 both POST and GET return byte-identical data for the demo order.
  • GET /v1/orders/{order_id} returns the mock only for the demo id sandbox-demo-order-001; any other id returns 404 NOT_FOUND — so you can exercise your not-found handling in sandbox exactly as in production.
  • The mock's tulemus.dokumendid[].allalaadimislink returns 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

MethodPathDescriptionAuth
POST/v1/signupSelf-service signup (sandbox key + payment link)None
POST/v1/scopingNIS2 applicability checkAll roles
GET/v1/scoping/questionsScoping question keysAll roles
GET/v1/framework-mappingAll NIS2 art 21(2) ↔ ISO/DORA/GDPRlawyer_*, grc_platform
GET/v1/framework-mapping/{key}Single measure mappinglawyer_*, grc_platform
POST/v1/incident-templates24h/72h/30d notification templatesAll except grc_platform
GET/v1/wizard/questionsGap-analysis questions (localised)All roles
POST/v1/wizard/evaluateGap-analysis evaluationAll roles
GET/v1/servicesService catalogue with pricesAll roles
POST/v1/ordersCreate order + payment linkAll roles
GET/v1/orders/{order_id}Order status + download linksAll roles
GET/v1/download/{token}Download document (token-based, no auth)Token
POST/v1/account/vatSet billing/VAT detailsAll roles
GET/v1/billing/summaryInvoices, subscription, usageAll roles
GET/v1/healthAPI health check (no auth)None
GET/v1/statusFull service health (no auth)None
GET/v1/changelogKB version changelog (no auth)None
Localisation. Endpoints with human-readable output accept a language selector — 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 (24 live EU countries)
  "keel": "en",           // response language ("en" | "et" | "de" | "fi" | ...)
  "tegevuskood": "86101"  // optional: national activity code (EMTAK/WZ/PKD/... or NACE 2.1) — a confirmed match pre-fills "sektor" when it is omitted; unknown code changes nothing
}

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))."],
    "riigi_overlay": {              // national-law layer — present only where the national act's own classification provision is covered (verbatim, sha256-locked KB); null = Directive baseline
      "termin": "wichtige Einrichtung",  // official term, always in the national language (never translated)
      "act": "BSIG", "viide": "§ 28", "kb_id": 989
    },
    "riigi_markus": "In the national transposition law (BSIG § 28), the official term for this category is 'wichtige Einrichtung'.",
    "kood_vaste": null,             // confirmed activity-code match (when "tegevuskood" was sent and recognised); null otherwise
    "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 (aj), 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": "…" }
}
There is no 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 (a1j2, 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_staatusdokumendi_staatusMeaning
ootelootelWaiting for payment
tasutudgenereerimiselPaid; generating documents
tasutudvabastatudDocuments 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": [ ... ]}}

Language. 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.
Structured details. Validation errors (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).
HTTPcodeWhen
401UNAUTHORIZEDMissing, unknown, or blocked API key
403FORBIDDEN_ROLEYour role is not permitted for this endpoint
404NOT_FOUNDResource does not exist (order, file, measure key)
409IDEMPOTENCY_CONFLICTDuplicate idempotency_key with different payload
422VALIDATION_FAILEDInvalid request body or parameters
429RATE_LIMITEDToo many requests; check Retry-After header
500INTERNAL_ERRORUnexpected server error; contact support
503UPSTREAM_UNAVAILABLEDependent service temporarily unavailable

On rate limiting, the response includes headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, Retry-After.

Rate Limits

TierReq/minReq/month
Baas (no subscription)602 000
Professional (990 EUR/mo)30050 000
Enterprise (2 900 EUR/mo)1 200500 000
Sandbox (nis2_test_*)30050 000

Every response includes X-RateLimit-* headers so you can track consumption programmatically. Each authenticated API key is tracked independently.

Pricing

All prices are net EUR. Full packages include legally-grounded documents in the target country's official language, signed by a local partner lawyer.
ServicePrice (EUR net)
Compliance Document Package1 590 – 2 490 (R1–R4)
Annual Maintenance290/month or 2 900/year prepaid
B-Scan (gap assessment)290
Framework mapping / incident390
Single document890
Supply-chain attestation1 590 – 3 390 (R1–R4)
Re-sign (document update)490
Combo discountmax 10 %
API licence Professional990 / month
API licence Enterprise2 900 / month

What the licence includes — and what it does not

Without licenceProfessionalEnterprise
API access: scoping, gap analysis, incident templates, framework-mapping queries (rate limit + monthly request quota)IncludedIncluded — higher quota, SLA, priority queue
Documents at 390 (framework-mapping document, incident-notification template pack)390 each195 each for up to 10 documents/month; thereafter 390 each195 each for up to 30 documents/month; thereafter 390 each
Single document from the package at 890890 each445 each for up to 10 documents/month; thereafter 890 each445 each for up to 30 documents/month; thereafter 890 each
Larger products (document package, B-scan, supply-chain attestation, re-sign, maintenance)Always full price — regardless of licence
The licence includes no documents free of charge or without limit. Every document is always a separate order with separate prepayment — the licence discount changes only the unit price — −50% of that document's own full price (195 instead of 390; 445 instead of 890) — up to the monthly quota; beyond it, orders simply revert to full price. Example: 10 framework-mapping documents per month on a Professional licence cost 10 × 195 = 1 950 EUR instead of 10 × 390 = 3 900 EUR — a saving of 1 950 EUR per month at full quota. The discount quota (10 on Professional, 30 on Enterprise) is shared across all discounted document types combined and resets at the start of each calendar month (UTC) — not on a rolling 30-day window.
VAT. All prices are net (excluding VAT); invoices are issued by AIPOS OÜ (Estonia). Estonian clients: +24% Estonian VAT. Business clients in another EU country with a valid VAT number (VIES-verified): reverse charge — 0% on the invoice, you account for VAT in your own country (Art. 196 of the VAT Directive); without a valid VAT number, Estonian VAT of 24% is added. Clients outside the EU: no Estonian VAT (export of services, 0%).

Payment rule: All orders are prepaid via Revolut to AIPOS OÜ. Documents are released only after payment confirmation. No credit terms.

→ SLA & versioning policy