> For the complete documentation index, see [llms.txt](https://docs.cloudcyte.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.cloudcyte.com/admin-guide/help/api-docs.md).

# API Documentation

Read your organisation's artifact data (the same records you see in the grids) from your own scripts and tools.

* **Base URL:** `https://<your-cybercyte-host>/grid-api`
* **Authentication:** send your organisation's API key in the `x-api-key` header on every request.
* **Format:** JSON in, JSON out. `POST` bodies use `Content-Type: application/json`.
* **Interactive reference:** *Help → API Documentation* in the product lists every field and lets you try the calls.

```bash
export CC_HOST="https://<your-cybercyte-host>"
export CC_KEY="<your-api-key>"
```

A typical integration takes three steps: find the artifact, check its columns, then read its data.

| Endpoint                           | Purpose                                                  |
| ---------------------------------- | -------------------------------------------------------- |
| `GET /grid-api/artifacts`          | List the artifact types you can read                     |
| `GET /grid-api/columns/{artifact}` | Get the columns of one artifact type                     |
| `POST /grid-api/columns-batch`     | Get the columns of several artifact types at once        |
| `POST /grid-api/data/{artifact}`   | Read a page of records, with filters, search and sorting |

***

### 1. List artifact types

`GET /grid-api/artifacts` (or `POST` with the same fields as a JSON body)

| Parameter        | Description                                                                    |
| ---------------- | ------------------------------------------------------------------------------ |
| `search`         | Words that must all appear in the artifact's name or table name, ignoring case |
| `parent`         | Only artifacts in this group, e.g. `Threat Analytics`, `Windows`, `GRC`        |
| `includeColumns` | `true` to include each artifact's columns in the same response                 |

```bash
curl -s "$CC_HOST/grid-api/artifacts?search=threat%20monitoring&parent=Threat%20Analytics" \
  -H "x-api-key: $CC_KEY"
```

```json
{
  "doc": [
    {
      "key": "windows-threat-monitoring-events",
      "artifact": "Windows Threat Monitoring Events",
      "table": "forensics_windows_threat_monitoring_events",
      "label": "Windows Threat Monitoring Events",
      "parent": "Threat Analytics",
      "columnCount": 74,
      "view": true
    }
  ],
  "len": 1
}
```

Pass the `artifact` value, or the `table` value, to the other endpoints. URL-encode it in paths, e.g. `Windows%20Threat%20Monitoring%20Events`.

***

### 2. Get an artifact's columns

`GET /grid-api/columns/{artifact}`

```bash
curl -s "$CC_HOST/grid-api/columns/Windows%20Threat%20Monitoring%20Events" \
  -H "x-api-key: $CC_KEY"
```

```json
{
  "doc": {
    "artifact": "Windows Threat Monitoring Events",
    "table": "forensics_windows_threat_monitoring_events",
    "columns": [
      { "field": "last_update_date", "label": "Last Update Date", "type": "datetime", "sortable": true, "searchable": false, "hidden": false },
      { "field": "hostname", "label": "Hostname", "type": "string", "sortable": true, "searchable": true, "hidden": false },
      { "field": "risk_score", "label": "Risk Score", "type": "number", "sortable": true, "searchable": false, "hidden": false },
      { "field": "tags", "label": "Tags", "type": "array", "sortable": false, "searchable": false, "hidden": false }
    ]
  }
}
```

Use `field` names in `selectFields`, `sort`, `searchFields` and `filters`. Columns with `hidden: false` are the ones returned by default.

**Several artifacts at once:** `POST /grid-api/columns-batch`

```bash
curl -s -X POST "$CC_HOST/grid-api/columns-batch" \
  -H "x-api-key: $CC_KEY" -H "Content-Type: application/json" \
  -d '{ "artifacts": ["Windows Threat Monitoring Events", "Windows Threat Monitoring Scripts"] }'
```

The response is keyed by exactly the names you sent. An unknown name is left out rather than failing the call.

***

### 3. Read data

`POST /grid-api/data/{artifact}`. The body is optional: without one, you get the first page of default columns, newest first.

| Field                     | Description                                                                 |
| ------------------------- | --------------------------------------------------------------------------- |
| `selectFields`            | Columns to return, in this order                                            |
| `filters`                 | Conditions the records must match (see below)                               |
| `search` / `searchFields` | Free-text search, optionally limited to some columns                        |
| `sort`                    | `{ "<column>": "asc" \| "desc" }`, or an array of these for several columns |
| `pageSize` / `pageOffset` | Page size (1–1000) and number of records to skip                            |

#### Example: high-risk threat events from the last 30 days

High or critical risk (`risk_score` 71 and above) **or** a high AI risk score, tagged as malicious or suspicious, sorted by risk, then newest first:

```bash
curl -s -X POST "$CC_HOST/grid-api/data/Windows%20Threat%20Monitoring%20Events" \
  -H "x-api-key: $CC_KEY" -H "Content-Type: application/json" \
  -d '{
    "selectFields": ["last_update_date", "hostname", "ip_address", "risk_score", "ai_risk_score", "matched_rule", "Image", "tags"],
    "filters": {
      "AND": [
        { "last_update_date": { "gte": "NOW-30d" } },
        { "OR": [ { "risk_score": { "gte": 71 } }, { "ai_risk_score": { "gte": 80 } } ] },
        { "tags": { "anyin": ["Malicious Activity", "Suspicious Activity"] } }
      ]
    },
    "sort": [ { "risk_score": "desc" }, { "last_update_date": "desc" } ],
    "pageSize": 50,
    "pageOffset": 0
  }'
```

```json
{
  "cols": ["last_update_date", "hostname", "ip_address", "risk_score", "ai_risk_score", "matched_rule", "Image", "tags"],
  "doc": [
    ["2026-09-27T08:14:03.512Z", "WS-FIN-014", "10.20.30.41", 100, 90, "Malicious File Hash Detected", "C:\\Users\\Public\\tool64.exe", ["Malicious Activity"]],
    ["2026-09-21T16:42:37.088Z", "LT-HR-202", "10.20.44.17", 85, 82, "Encoded PowerShell Command", "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", ["Suspicious Activity"]]
  ],
  "len": 2
}
```

Each record in `doc` is an **array** whose values line up with `cols`. `len` is the total number of matches across all pages (counting stops at 10,000). To get the next page, add `pageSize` to `pageOffset`.

> A `risk_score` of exactly **70** means the record has not been classified yet. To get elevated risk only, use `gte: 71`.

#### More examples

Search for a process name, limited to the image path column:

```bash
curl -s -X POST "$CC_HOST/grid-api/data/Windows%20Threat%20Monitoring%20Events" \
  -H "x-api-key: $CC_KEY" -H "Content-Type: application/json" \
  -d '{ "search": "powershell", "searchFields": ["Image"], "selectFields": ["hostname", "Image", "risk_score"], "pageSize": 20 }'
```

Unacknowledged critical events since a fixed date:

```bash
curl -s -X POST "$CC_HOST/grid-api/data/Windows%20Threat%20Monitoring%20Events" \
  -H "x-api-key: $CC_KEY" -H "Content-Type: application/json" \
  -d '{ "filters": { "risk_score": { "gte": 90 }, "is_acknowledged": 0, "last_update_date": { "gte": "2026-09-01" } } }'
```

#### Filter reference

| What         | Syntax                                                                                                                                                              |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Equals       | `{ "risk_score": 90 }`                                                                                                                                              |
| Operators    | `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `in`, `nin`: `{ "risk_score": { "gt": 70, "lte": 89 } }`                                                                      |
| Combine      | `{ "AND": [ … ] }`, `{ "OR": [ … ] }`, `{ "NOT": { … } }`, nestable. Several columns in one object must all match.                                                  |
| Scores       | `risk_score`, `ai_risk_score`, `exposure_risk_score`: any operator                                                                                                  |
| Dates        | `first_discovery_date`, `last_update_date`: a date (`"2026-09-01"`) or relative to now (`"NOW-30d"`, `"NOW-12H"`, `"NOW-1y2m"`). Units: `y` `m` `w` `d` `H` `M` `S` |
| Flags        | `is_acknowledged`, `is_malicious`, `is_shadow_it`: `0` or `1`                                                                                                       |
| Tags         | `has` (this tag), `anyin` (any of these), `allin` (only tags from this list)                                                                                        |
| Empty values | Records with no value are excluded by `ne` and `NOT`. Add `null` to a list to include them: `{ "in": [90, null] }`                                                  |

Conditions on columns the artifact doesn't have, unknown operators and wrongly typed values are ignored rather than rejected. If a filter seems to have no effect, check the column names against `/grid-api/columns/{artifact}`.

***

### Errors

| Status | Meaning                                                                                                                             |
| ------ | ----------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | A required field is missing or has the wrong type, e.g. `{ "err": "A non-empty \"artifacts\" (or \"tables\") array is required." }` |
| `401`  | The `x-api-key` header is missing, unknown or expired: `{ "err": "Wrong API key!" }`                                                |
| `404`  | Unknown artifact type: `{ "err": "The artifact type \"…\" was not found." }`                                                        |
| `500`  | Server-side error                                                                                                                   |
