# ISM Explorer API — AI Skill

> Machine-readable reference for AI agents interacting with the ISM Explorer API.
> Human docs: https://secctrl.fyi/api.html

## Base URL

```
https://api.secctrl.fyi
```

All endpoints are **GET**, return **JSON**, and require no authentication.

## Rate limits

All limits are per IP, per minute.

| Tier | Limit | Applies to |
|---|---|---|
| Standard | 60/min | `/api/stats`, `/api/versions`, `/api/sections`, `/api/guidelines` |
| Bulk | 10/min | `/api/controls`, `/api/terms`, `/api/principles` |
| History | 10/min | `/api/controls/:id/history` |
| Detail | 30/min | `/api/controls/:id`, `/api/controls/:id/graph`, `/api/terms/:id` |
| Compare | 2/min | `/api/compare` |

A 429 response includes `Retry-After: 60`. Pass `X-Bypass-Token: <token>` to skip rate limiting (tokens issued to IRAP practitioners on request).

---

## Endpoints

### GET /api/controls

List controls. All params optional.

| Param | Type | Notes |
|---|---|---|
| `search` | string | Full-text search, or exact ID (`ISM-1173`, `ism-1173`, `1173`) |
| `filter` | string | `all` (default) · `e8` · `ml1` · `ml2` · `ml3` · `changed` · `new` · `withdrawn` |
| `section` | string | Filter by ControlGroup ID |
| `guideline` | string | Filter by guideline name (exact match) |
| `applicability` | string | `NC` · `OS` · `P` · `S` · `TS` |

**Response** `{ controls: Control[], total: number }`

Each `Control`:
```json
{
  "id": "ism-1173",
  "display_id": "ISM-1173",
  "label": "1173",
  "statement": "Application control is implemented on ...",  // truncated to 200 chars
  "applicability": ["NC","OS","P","S","TS"],
  "e8_levels": ["ML1","ML2","ML3"],
  "change_type": "modified",
  "updated": "Sep-2024",
  "catalog_version": "ISM-2024-09",
  "guideline": "Guidelines for System Hardening",
  "section": "System Hardening",
  "section_id": "system-hardening"
}
```

**Tip:** Exact ISM ID lookups (`ISM-1173`, `ism-1173`, bare `1173`) are fast single-row matches. Free-text search uses a D1 LIKE query against `statement` and `display_id`.

---

### GET /api/controls/:id

Full detail for one control. `:id` is case-insensitive (`ISM-1173`, `ism-1173`, `1173`).

**Response:**
```json
{
  "id": "ism-1173",
  "display_id": "ISM-1173",
  "label": "1173",
  "control_class": "ISM-control",
  "section": "System Hardening",
  "section_id": "system-hardening",
  "section_overview": "...",
  "latest": {
    "catalog_version": "ISM-2024-09",
    "commit_date": "2024-09-15",
    "statement": "Application control is implemented on ...",
    "change_type": "modified",
    "applicability": ["NC","OS","P","S","TS"],
    "applicability_raw": null,
    "e8_levels": ["ML1","ML2","ML3"],
    "e8_strategies": [
      { "level": "ML1", "strategy": "application-control" }
    ],
    "revision": "9",
    "updated": "Sep-2024",
    "guideline": "Guidelines for System Hardening",
    "source": "oscal",
    "compliance": null
  },
  "history": [
    // lightweight — all fields EXCEPT statement; use /history for full text
    {
      "catalog_version": "ISM-2024-09",
      "commit_date": "2024-09-15",
      "change_type": "modified",
      "applicability": ["NC","OS","P","S","TS"],
      "applicability_raw": null,
      "e8_levels": ["ML1","ML2","ML3"],
      "revision": "9",
      "updated": "Sep-2024",
      "guideline": "Guidelines for System Hardening",
      "source": "oscal",
      "compliance": null,
      "change_complexity": "medium"
    }
  ],
  "annotation": {
    "ai_view": "Plain-English summary...",
    "ai_view_snarky": "Sardonic summary...",
    "links": [],
    "impls": [],
    "updated_at": "2025-02-23T10:00:00Z"
  }
}
```

- `control_class` values: `ISM-control` · `ISM-principle`
- `latest` contains the **current** revision with full `statement` text.
- `history[]` omits `statement` — use `/api/controls/:id/history` to get all historical text.
- `annotation` may be `null` if not yet generated (all controls annotated as of Feb 2026).
- `change_type` values: `new` · `modified` · `unchanged` · `withdrawn`
- `source` values: `oscal` (2022–present) · `pdf` (2010–2021, historical)
- `compliance` (PDF-era only): `must` · `should` · `recommended` · `null`
- `change_complexity` (AI-scored, `modified` rows only): `very-low` · `low` · `medium` · `high` · `null`

---

### GET /api/controls/:id/history

Full revision history with `statement` text at every ISM release back to November 2010. Rate-limited at 10/min to protect this unique dataset.

**Response:**
```json
{
  "id": "ism-1173",
  "history": [
    {
      "catalog_version": "ISM-2024-09",
      "commit_date": "2024-09-15",
      "statement": "Application control is implemented on ...",
      "change_type": "modified",
      "applicability": ["NC","OS","P","S","TS"],
      "applicability_raw": null,
      "e8_levels": ["ML1","ML2","ML3"],
      "revision": "9",
      "updated": "Sep-2024",
      "guideline": "Guidelines for System Hardening",
      "source": "oscal",
      "compliance": null,
      "change_complexity": "medium"
    }
    // ... more entries, newest first
  ]
}
```

---

### GET /api/controls/:id/graph

Cytoscape.js-formatted neighbourhood graph — the control plus its section siblings.

**Response:** `{ nodes: Node[], edges: Edge[], group: { id, title } }`

Each node `data`: `{ id, display_id, label, statement, role }` where `role` is `"center"` or `"neighbor"`.

---

### GET /api/compare

Controls that changed between two catalog versions.

| Param | Required | Notes |
|---|---|---|
| `from` | yes | Catalog version string, e.g. `ISM-2024-09` |
| `to` | yes | Must be later than `from` |

**Response:**
```json
{
  "from": "ISM-2024-09",
  "to": "ISM-2025-01",
  "changes": [
    {
      "id": "ism-1234",
      "display_id": "ISM-1234",
      "change_type": "modified",
      "guideline": "Guidelines for Network Management",
      "section": "Network Management",
      "new_statement": "...",
      "old_statement": "...",
      "new_applicability": ["OS","P","S"],
      "old_applicability": ["OS","P"],
      "change_complexity": "medium"
    }
  ],
  "total": 42
}
```

- `change_type` values: `new` · `modified` · `withdrawn`
- `new` entries: `old_statement` and `old_applicability` are `null`; `change_complexity` is `"unknown"`
- `withdrawn` entries: `new_statement` and `new_applicability` are `null`; `change_complexity` is `null`
- `change_complexity` for `modified`: highest AI-scored complexity across all intermediate changes in the range (`very-low` · `low` · `medium` · `high`); `null` if unscored

---

### GET /api/versions

All ISM catalog versions in the graph, oldest first.

**Response:** `[ { "version": "ISM-2022-04", "date": "2022-04-01" }, ... ]`

Use this to discover valid values for `from`/`to` in `/api/compare`.

---

### GET /api/stats

Current catalog summary.

**Response:** `{ "version": "ISM-2025-01", "controls": 1073, "principles": 18, "terms": 214 }`

---

### GET /api/principles

All principles (current revision).

**Response:** `{ principles: Principle[], total: number }`

---

### GET /api/sections

All ControlGroups (sections) that contain controls.

**Response:** `[ { "id", "title", "overview", "guideline", "control_count" }, ... ]`

---

### GET /api/guidelines

All guideline names with control counts.

**Response:** `[ { "guideline": "Guidelines for System Hardening", "control_count": 47 }, ... ]`

---

### GET /api/terms

Glossary terms. Optional `search` param for case-insensitive substring match on term name.

**Response:** `{ terms: [ { "id", "term", "meaning", "change_type", "catalog_version" } ], total }`

---

### GET /api/terms/:id

Term detail with full revision history.

**Response:** `{ "id", "term", "history": [ { "catalog_version", "commit_date", "meaning", "change_type" } ] }`

---

### GET /

Health check. Returns `{ "status": "ok", "service": "ism-explorer-api" }`.

---

## Applicability codes

| Code | Meaning |
|---|---|
| `NC` | Non-corporate Commonwealth entity |
| `OS` | OFFICIAL:Sensitive |
| `P` | PROTECTED |
| `S` | SECRET |
| `TS` | TOP SECRET |
| `C` | CONFIDENTIAL (historical, pre-2019 PDF era only) |

## Common workflows

**Find a control by ID:**
```
GET /api/controls/ism-1173
```

**Search for controls about a topic:**
```
GET /api/controls?search=multi-factor+authentication
```

**Get all Essential Eight controls at ML2:**
```
GET /api/controls?filter=ml2
```

**See what changed between two versions:**
```
GET /api/versions   # find version strings first
GET /api/compare?from=ISM-2024-09&to=ISM-2025-01
```

**Get the full revision history of a control (including statement text at every release):**
```
GET /api/controls/ism-1173/history
```

**Find all controls in a specific section:**
```
GET /api/sections                          # find section IDs
GET /api/controls?section=<section_id>
```
