Home›Cyresora›API
Cyresora · API

Cyresora API, as shipped in the download

Cyresora API

Every running deadline over HTTP, plus an MCP endpoint for agents. The machine-readable spec lives at /api/openapi.json.

Authentication

Mint a key in Settings -> API & agents. Every request carries it as a bearer token:

curl https://cyresora.example.com/api/deadlines \
  -H "Authorization: Bearer apk_xxxx"

Keys are scoped. Read-only or full, chosen when you mint it. A read-only key is refused any non-GET at the door, once, rather than in each route.

The one rule of this API

You cannot set a deadline. There is no due field anywhere, deliberately. You record two facts — when you became aware and when you actually submitted — and the instrument decides the rest. The one-month final-report clocks run from the submission instant, and the CRA's 14-day final report does not start at all until a corrective measure exists.

All times are UTC to the second. A 24-hour deadline cannot survive a timezone left implicit.

Endpoints

GET /api/pingVerify the key
GET /api/deadlinesEvery running deadline, worst first, hours remaining as a number
GET /api/incidentsThe register with every regime ladder. `?state=overdue\due_soon\open\pending`
POST /api/incidentsOpen an incident; the clocks start from aware_at_utc
GET /api/incidents/{id}One incident with its ladders and everything sent
POST /api/incidents/{id}/notificationsRecord a notification you have sent
GET /api/vulnerabilitiesThe vulnerability register, actively exploited first
GET /api/incidents/{id}/draft?regime=&stage=A stage's report draft from the record, gaps listed in to_complete (1.1.0; records nothing)
GET /api/risksThe risk register, highest score first, with the NIS2 Art. 21(2) areas holding no open risk (1.1.0)
GET /api/documentsThe document hub: each document's version in force, its SHA-256 and review state (1.1.0)
GET /api/openapi.jsonThe spec

The endpoint a monitoring system polls

curl https://cyresora.example.com/api/deadlines -H "Authorization: Bearer apk_xxxx"
{
  "now_utc": "2026-09-14 18:00:00",
  "deadlines": [
    { "reference": "INC-20260914-A1B2", "regime": "cra_incident",
      "stage": "early", "label": "Early warning", "provision": "Article 14(3)",
      "due_utc": "2026-09-15 08:00:00", "hours_left": 14, "state": "open",
      "counted_from": "becoming aware", "deadline_at_weekend": false }
  ]
}

hours_left is signed: negative means the deadline has passed. state is one of overdue, due_soon, open or pending — and pending means the clock has not started, with counted_from naming the event that will start it.

Open an incident

curl -X POST https://cyresora.example.com/api/incidents \
  -H "Authorization: Bearer apk_xxxx" -H "Content-Type: application/json" \
  -d '{"title":"Exploited auth bypass in the gateway",
       "aware_at_utc":"2026-09-14 08:00:00",
       "regimes":["cra_incident","nis2"],
       "severity":"critical","malicious_suspected":true,"cross_border":true}'

A date with no time is refused — a 24-hour deadline cannot be computed from one. An incident with no regime is refused, because it would have no clock.

Regimes: cra_vuln, cra_incident, nis2, and since 1.1.0 gdpr and dora. For those two, send personal_data_breach_aware_at_utc (GDPR's anchor; left out, aware_at_utc is used) and classified_major_at_utc (DORA's: the initial notification is due 4 hours after it, and no later than 24 hours after awareness when classified within them). DORA stage keys: initial, intermediate, final; GDPR: notify.

Record a notification

curl -X POST https://cyresora.example.com/api/incidents/1/notifications \
  -H "Authorization: Bearer apk_xxxx" -H "Content-Type: application/json" \
  -d '{"regime":"nis2","stage":"notify","submitted_at_utc":"2026-09-16 00:00:00",
       "recipient":"BSI","channel":"Portal","reference":"BSI-INC-88213"}'

Needs notify.submit. Refused with 422 if the stage is unknown, the regime is not running on that incident, or the time is before awareness or in the future; 409 if that stage is already recorded — a duplicate is not a second duty discharged.

Webhooks

incident.opened, notification.recorded and deadline.alert, HMAC-signed with X-Cyresora-Signature: sha256=... over the raw body. notification.recorded fires however the filing was recorded — browser, REST or agent. deadline.alert fires at each of the owner's alert points before a reporting deadline, and once when it passes, with incident, regime, stage, due_utc, hours_left, alert_point_hours and overdue.

Connect it to an assistant

Mint the key in the app first: Settings → API & agents. Choose Read only when the assistant should answer questions but never change anything — the endpoint then lists only the read tools and refuses the rest by name, so a careless prompt cannot write. Full access behaves as before.

Every client needs the same three facts, and nothing in the handshake is vendor-specific:

The addresshttps://cyresora.example.com/mcp
The keyheader Authorization: Bearer apk_xxxx
The transportMCP over streamable HTTP, stateless

Claude — one command, or the same URL and header as a custom connector in the desktop and web apps:

claude mcp add --transport http cyresora https://cyresora.example.com/mcp \
  --header "Authorization: Bearer apk_xxxx"

ChatGPT and the OpenAI API — one entry in the Responses API's tools array (in ChatGPT itself, the same URL and key go in as a connector):

{
  "type": "mcp",
  "server_label": "cyresora",
  "server_url": "https://cyresora.example.com/mcp",
  "authorization": "apk_xxxx",
  "require_approval": "never"
}

**Every other client spells the same three facts differently — copy the shape from its own documentation, not from here.** VS Code is the clearest example of why: its configuration reference (read 6 September 2026) puts servers in .vscode/mcp.json under a "servers" object — *"an object that maps server names to their configurations"*. Pasted as-is, a block written for another client will not load there. The id, the URL and the token are what travel; the JSON around them belongs to whichever client you are configuring.

A local model, n8n, or your own code — n8n's MCP Client node takes the URL and the same Authorization: Bearer header; a model running on your own machine reaches it through any MCP client, so nothing leaves your network at all. Writing it yourself is one POST of JSON-RPC 2.0:

curl -X POST https://cyresora.example.com/mcp \
  -H "Authorization: Bearer apk_xxxx" -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

Any client that speaks MCP's streamable-HTTP transport works, including ones that do not exist yet: Cyresora implements the protocol, not an integration with a particular vendor.

Tools

toolaccesswhat it does
deadlinesreadEvery running clock, worst first, with hours remaining and what each is counted from. Start here.
list_incidentsreadThe register with every regime ladder
get_incidentreadOne incident in full, with everything recorded against it
what_appliesreadEach regime's stages, limits, anchors and application date — so an agent explains the duty without guessing
open_incidentwriteOpen an incident; the clocks start from the awareness time you give
record_notificationwriteRecord a notification already sent
draft_reportreadA stage's draft from the incident record, gaps marked; for a person to send (1.1.0)
list_risksreadThe risk register and the Article 21(2) areas with no open risk (1.1.0)
list_documentsreadThe document hub: versions in force, hashes, review state (1.1.0)

Read tools are declared readOnly, so a read-only key does not see the write tools in tools/list and is refused them by name on tools/call.

What the agent is told, in the endpoint's own instructions: it cannot set a deadline; it must never record a notification as sent unless the human has said it was sent; it should say plainly when a deadline falls at a weekend, because the clocks do not pause; and NIS2's binding deadlines are the Member State's, which this register does not model.

Connect it to Zapier, Make or n8n

Cyresora talks to other tools without a plugin: it sends a webhook (an HTTPS message) the moment something happens, and it accepts calls to the API above. Zapier, Make and n8n can use both.

Before you start

  • In Cyresora, open Settings -> API & agents. Mint an API key (choose Read only if the other tool only reads), and add a webhook when you have its URL (below).
  • Use your install's full address, for example https://cyresora.yourcompany.com.

When something happens in Cyresora: send it to another tool

  1. In the other tool, create the trigger that gives you a URL to receive webhooks: - Zapier: the "Webhooks by Zapier" app, trigger Catch Hook (Webhooks by Zapier needs a paid Zapier plan). - Make: module Webhooks → Custom webhook. - n8n: the Webhook node.
  2. Copy that URL into Cyresora under Settings -> API & agents, and choose the event or events below.
  3. Make the event happen once in Cyresora, so the other tool receives a sample and you can map its fields.
EventFires when
incident.opened, and , HMAC-signed with
notification.recorded, and , HMAC-signed with
deadline.alert, and , HMAC-signed with

Every delivery carries X-Cyresora-Signature, an HMAC-SHA256 signature over the raw body. Zapier and Make accept the delivery without checking it, so keep the webhook URL private. In n8n you can check it in a Code node before using the data.

When something happens elsewhere: act in Cyresora

  • Zapier: "Webhooks by Zapier", action POST (or Custom Request).
  • Make: module HTTP → Make a request.
  • n8n: the HTTP Request node.

Send the request to your install's address plus the path from the Endpoints table above, with the header Authorization: Bearer <your API key> and a JSON body. A read-only key cannot write.

The endpoints that change something:

  • POST /api/incidents: Open an incident; the clocks start from aware_at_utc
  • POST /api/incidents/{id}/notifications: Record a notification you have sent

Example recipes

  • incident.opened → a row in Google Sheets: webhook trigger on incident.opened (, and , HMAC-signed with), then the Google Sheets "Create row" action with the fields you want.
  • notification.recorded → a message in Slack or Teams: webhook trigger on notification.recorded (, and , HMAC-signed with), then the Slack or Microsoft Teams "Send message" action.
  • A new form entry or row elsewhere → Cyresora: the other tool's trigger (for example a new Google Sheets row or a form submission), then an HTTP action calling POST /api/incidents (Open an incident; the clocks start from aware_at_utc).

These recipes use only the events and endpoints documented above. We have not run them inside Zapier, Make or n8n ourselves; Cyresora's own automated tests check its API.

← Back to Cyresora · Manual · Quickstart · Test run