Skip to content

ENISA Reporting — REST API & Swagger

The ENISA SRP module exposes a versioned OpenAPI 3.1 REST API with a bundled Swagger UI, all under /api/v1/enisa. The OpenAPI document and the routes are generated from the same handlers (via utoipa), so they never drift.

PathMethodAuthPurpose
/api/v1/enisa/notificationsPOSTCreate a notification case (vulnerability or incident)
/api/v1/enisa/notificationsGETList cases
/api/v1/enisa/notifications/{id}GETCase with full revision history
/api/v1/enisa/notifications/{id}/revisionsPOSTSubmit a staged revision (validated)
/api/v1/enisa/notifications/{id}/revisionsGETRevision history
/api/v1/enisa/obligationsGETThe versioned obligation matrix
/api/v1/enisa/openapi.jsonGETOpenAPI 3.1 document
/api/v1/enisa/swagger-uiGETSwagger UI
/api/v1/enisa/graphqlPOSTGraphQL queries (see GraphQL)
/api/v1/enisa/graphql/playgroundGETGraphQL playground

The documentation surface (Swagger UI, OpenAPI doc, GraphQL playground) is public so it loads in a browser; the data surface requires a FLEET API key.

Authentication, scopes & the reporter identity

Section titled “Authentication, scopes & the reporter identity”

Data endpoints use FLEET’s standard API-key auth:

Authorization: Bearer fleet_xxxxxxxx…

Each request is checked for the appropriate scope:

OperationRequired scope
Reads (GET …, and GraphQL queries)enisa:read
Writes (create case, submit revision)enisa:write

A wildcard (*) key satisfies either. A valid key with the wrong scope gets 403 (Missing required scope: …); a missing/invalid key gets 401.

The full catalogue of selectable scopes (including enisa:read/enisa:write) is available at GET /api/v1/admin/scopes (requires admin:read), or via the fleet_list_scopes MCP tool — use it when minting keys with fleet_create_api_key or POST /api/v1/admin/api-keys.

On success, FLEET injects the authenticated key’s name as the reporting entity (field 6, “automated”) via the x-enisa-reporter header. This value overrides any reporter in the request body, so the reporter cannot be spoofed.

Terminal window
curl -X POST https://your-fleet/api/v1/enisa/notifications \
-H "Authorization: Bearer $FLEET_KEY" -H "content-type: application/json" \
-d '{"notification_type":"vulnerability"}'
# → { "id": "…", "notification_type": "vulnerability", "reporter": "…", … }

Only the obligatory fields for this stage are required (see Obligation Matrix).

Terminal window
curl -X POST https://your-fleet/api/v1/enisa/notifications/$ID/revisions \
-H "Authorization: Bearer $FLEET_KEY" -H "content-type: application/json" \
-d '{
"stage": "early_warning_24h",
"manufacturer_name": "ACME Corp",
"product": "Widget Server",
"title": "Actively exploited RCE in config parser",
"cve_id": "CVE-2026-0001",
"member_states": ["DE", "FR"]
}'

Fields from the 24h warning (title, CVE, manufacturer…) are carried forward automatically — you only send what’s new.

Terminal window
curl -X POST https://your-fleet/api/v1/enisa/notifications/$ID/revisions \
-H "Authorization: Bearer $FLEET_KEY" -H "content-type: application/json" \
-d '{
"stage": "update_72h",
"vuln_nature": "Heap overflow in the configuration parser",
"exploit_nature": "Remote, unauthenticated, via crafted config",
"vuln_corrective_taken": "Hotfix 1.2.3 released",
"vuln_user_measures": "Upgrade to 1.2.3; restrict config sources"
}'
Terminal window
curl -X POST https://your-fleet/api/v1/enisa/notifications/$ID/revisions \
-H "Authorization: Bearer $FLEET_KEY" -H "content-type: application/json" \
-d '{
"stage": "final",
"vuln_measure_available_date": "2026-06-10",
"vuln_severity": "Critical (CVSS 9.8)",
"vuln_impact": "Full host compromise",
"vuln_security_update_details": "Fixed in 1.2.3; backported to 1.1.9"
}'

Stages must not go backwards: submitting an earlier stage than the latest recorded one returns 409 STAGE_REGRESSION.

Required-ness is stage-specific. A missing obligatory field returns 422 with a structured list of violations:

{
"error": {
"code": "VALIDATION_FAILED",
"message": "one or more fields are invalid for this stage",
"violations": [
{ "field_id": "12", "stage": "early_warning_24h",
"kind": "missing_obligatory", "message": "field 12 is required at this stage" }
]
}
}

Submitting a field that belongs to the other notification type (e.g. incident data on a vulnerability case) yields a not_applicable violation.

StatuscodeWhen
404NOT_FOUNDUnknown notification id
409STAGE_REGRESSIONNew stage is earlier than the latest recorded stage
422VALIDATION_FAILEDObligatory field missing, or field not applicable to the type

Run the module standalone (no auth) for exploration:

Terminal window
DATABASE_URL=postgres://you@localhost/fleet_test \
cargo run -p enisa-srp --example serve
# http://127.0.0.1:8099/swagger-ui · /openapi.json