# Workspace API reference

The workspace endpoints you can call with API keys today, to validate, plan, apply and export detection content, download the CLI and send external alerts.

These are the endpoints of your workspace that accept [API keys](https://tracix.io/docs/api/authentication). Everything else in the workspace serves the browser. There is no public API yet for querying telemetry or for managing organizations and workspaces.

## Conventions

| Convention     | Detail                                                                                                           |
| -------------- | ---------------------------------------------------------------------------------------------------------------- |
| Base URL       | Your workspace's address, such as `https://production-a7k3p2q9.app.tracix.io`. The examples call it `TRACIX_URL` |
| Authentication | `X-API-Key: txk_...` on every request, over HTTPS                                                                |
| Bodies         | JSON, with `Content-Type: application/json`                                                                      |
| Errors         | A status code and a JSON body with an `error` message                                                            |
| Times          | UTC, in ISO 8601                                                                                                 |

| Status | Meaning                                                 |
| ------ | ------------------------------------------------------- |
| `401`  | The key is missing, unknown, expired or revoked         |
| `403`  | The key doesn't carry the permission the endpoint needs |

## Detection content

These endpoints take a CI key. They back the `tracix-detections` CLI; see [Monitors, dashboards and detections as code](https://tracix.io/docs/api/as-code) for the repository format.

### Validate, plan or apply

`POST /api/security/detections` takes the files of a detection repository at one commit, or a reference the workspace fetches itself, and validates, plans or applies them.

| Field        | Type    | Description                                                                                                                                                          |
| ------------ | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `action`     | string  | Required. `validate`, `plan` or `apply`                                                                                                                              |
| `files`      | object  | The repository's files as `{"path": "content"}`. Send `files` or `archiveRef`, not both                                                                              |
| `revision`   | string  | Required with `files`: the commit the files were taken at, up to 128 characters                                                                                      |
| `subdir`     | string  | The folder that holds the detections, when it isn't the repository root                                                                                              |
| `archiveRef` | object  | `{"credentialId", "repo", "ref", "subdir"}`: the workspace fetches this commit from GitHub or GitLab with a credential stored in the workspace. `subdir` is optional |
| `package`    | string  | Refuse the request unless the manifest publishes this package                                                                                                        |
| `partial`    | boolean | `validate` only: the files are one batch of a larger repository                                                                                                      |
| `planHash`   | string  | Required for `apply`: the `planHash` of the plan you reviewed                                                                                                        |
| `replace`    | boolean | `apply` only: confirm the plan's conflicts, replacing the rules they name                                                                                            |

`validate` and `plan` need `security.read`; `apply` needs `security.manage`. Request bodies are limited to 10 MB: for a larger repository, send `archiveRef`.

```bash
curl -fsS -X POST "$TRACIX_URL/api/security/detections" \
  -H "X-API-Key: $TRACIX_API_KEY" \
  -H "Content-Type: application/json" \
  -d @request.json
```

```json title="request.json"
{
  "action": "plan",
  "revision": "4f2c9e1",
  "files": {
    "tracix-detections.yaml": "package: acme-soc\nprune: true\n",
    "lists/service-accounts.yaml": "name: service-accounts\nitems:\n  - svc_backup\n"
  }
}
```

**Responses**

| Action     | Status | Body                                                                                                                                                                                                                                                             |
| ---------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `validate` | `200`  | `package`, `revision`, `valid`, `summary` (counts of files, rules, lists, YARA rulesets, disabled rules, errors, warnings and fixture results), `diagnostics`, `files`, `fixtures`, `ignored` and `source`                                                       |
| `plan`     | `200`  | Everything `validate` returns, plus `planHash` (`null` when the plan can't be applied), `blocking`, `planWarnings`, `counts`, `resources` (one row per object, with its `action`: `create`, `update`, `delete`, `unchanged` or `conflict`) and `hiddenResources` |
| `apply`    | `200`  | `applied: true`, `package`, `revision`, `planHash`, `counts` (`created`, `updated`, `deleted`, `unchanged`, `replaced`), `results`, `postApply` and `source`                                                                                                     |
| `apply`    | `202`  | More than 200 writes run in the background: `{"job": {"jobId", "status", "total"}, "source"}`. Follow the job with the next endpoint                                                                                                                             |

`valid` is `false` when a file doesn't compile or a fixture fails. `source` says where the files came from: `{"kind": "files"}`, or the code host, repository and commit for `archiveRef`.

**Errors**

| Status | When                                                                                                                                                                                              |
| ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | The body isn't valid JSON, an unknown `action`, `partial` outside `validate`, a missing or malformed `planHash`, a `package` that doesn't match the manifest, or a plan that compile errors block |
| `404`  | `archiveRef`: the credential, the repository or the commit can't be found or read                                                                                                                 |
| `409`  | The content or the workspace changed since the plan, so plan again; the plan has conflicts you haven't confirmed; or another apply is running                                                     |
| `413`  | The body is larger than 10 MB, or the repository exceeds 5,000 detection files or 50 MB                                                                                                           |
| `502`  | The code host refused the request, or the change wasn't accepted. Try again                                                                                                                       |

### Follow a background apply

`GET /api/security/detections?job=<jobId>` returns the progress of an apply that runs in the background. It needs `security.read`.

The response is `{"job": {...}}` with `jobId`, `package`, `revision`, `planHash`, `status` (`running`, `succeeded`, `failed` or `interrupted`), `total`, `done`, `counts`, `results`, `startedAt`, `updatedAt` and, once finished, `finishedAt`. A job that failed or was interrupted also has an `error`, and a failed one the `failedResource` it stopped at. It answers `400` without a valid job ID and `404` for an unknown one.

### Download the CLI

`GET /api/security/detections/cli` returns the `tracix-detections` CLI that matches this workspace, as `tracix-detections.mjs`. It needs `security.read`. The `X-Tracix-Cli-Sha256` response header carries the file's SHA-256, so a pipeline can check it before running it.

```bash
curl -fsS -H "X-API-Key: $TRACIX_API_KEY" -D cli.headers \
  "$TRACIX_URL/api/security/detections/cli" -o tracix-detections.mjs
```

### Export detection content

`GET /api/security/detections/export` returns detection content as repository files. It needs `security.read`, and a key sees every rule and list.

| Query                     | Description                                                                                                                        |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `package`                 | Required. The package to export, or for `source=ui`, the package name for the new files                                            |
| `source`                  | `package` (the default) exports the rules and lists a package publishes. `ui` exports rules authored in the UI as a new repository |
| `ids`, `tag`, `technique` | `source=ui` only: which rules. Repeat a parameter or separate values with commas; `ids` takes up to 500                            |
| `pack`                    | `source=ui`: `include` adds rules installed from the Sigma pack to a `tag` or `technique` selection                                |
| `lists`                   | `source=ui`: `include` also writes the value lists the rules use                                                                   |
| `format`                  | `zip` returns a ZIP archive instead of JSON                                                                                        |

A package export returns `package`, `revision`, `files` (`{"path": "content"}`), `resources` (each with `exact: true` when compiling the file reproduces what the workspace runs), `summary` and `notes`. Fixtures aren't stored in the workspace, so they aren't exported. It answers `404` when nothing is published under the package; a `source=ui` export answers `404` when no rules match and `422` when none of them can be exported.

## External alerts

These endpoints take an API key of the ingest type, with any `write:` permission. Alerts they receive open and resolve alert episodes alongside your own monitors'.

### Receive Alertmanager alerts

`POST /api/ingest/alertmanager` takes the payload Prometheus Alertmanager sends to a webhook receiver. Send the API key in the `X-API-Key` header.

| From the Alertmanager alert                                                           | Becomes                                                                                                                      |
| ------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `status`                                                                              | `firing` opens or re-asserts an episode; `resolved` recovers it                                                              |
| `fingerprint`                                                                         | The identity of the alert stream                                                                                             |
| The first of the labels `instance`, `host`, `hostname`, `service`, `pod`, `alertname` | The entity                                                                                                                   |
| The `severity` label                                                                  | `critical` for `critical`, `page`, `p1`, `error` or `fatal`; `warning` for `warning`, `warn`, `p2` or `p3`; `info` otherwise |
| The `summary` or `description` annotation                                             | The reason                                                                                                                   |

The response is `{"ok": true, "opened", "reasserted", "resolved", "ignored"}`.

### Send alert events

`POST /api/ingest/events` takes events from your own scripts, relays and third-party systems.

| Field                | Type   | Description                                                                                             |
| -------------------- | ------ | ------------------------------------------------------------------------------------------------------- |
| `events`             | array  | Required. Up to 100 events per request; more are ignored                                                |
| `events[].dedupeKey` | string | Required. Identifies the alert stream: events with the same `source` and `dedupeKey` update one episode |
| `events[].status`    | string | Required. `firing` or `resolved`                                                                        |
| `events[].entity`    | string | Required. What the alert is about, such as a host or service name                                       |
| `events[].source`    | string | Up to 32 letters, digits, `_` or `-`. Defaults to `webhook`                                             |
| `events[].title`     | string | Defaults to `<source>: <entity>`                                                                        |
| `events[].reason`    | string | Defaults to `external alert`                                                                            |
| `events[].severity`  | string | Mapped the same way as Alertmanager's `severity` label                                                  |
| `events[].labels`    | object | Up to 10 string labels                                                                                  |

Events missing a required field are dropped. Long values are cut to fit: 256 characters for `dedupeKey` and `entity`, 300 for `title` and 1,000 for `reason`.

```bash
curl -fsS -X POST "$TRACIX_URL/api/ingest/events" \
  -H "X-API-Key: $TRACIX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"events": [{"dedupeKey": "backup-nightly", "status": "firing", "entity": "db-01", "source": "backup", "severity": "warning", "reason": "Nightly backup did not finish"}]}'
```

```json title="Response"
{ "ok": true, "received": 1, "dropped": 0, "opened": 1, "reasserted": 0, "resolved": 0, "ignored": 0 }
```

It answers `400` when the body has no `events` array or no valid event.

## Next steps

- [Authenticate with API keys](https://tracix.io/docs/api/authentication): Create the keys these endpoints take

- [Run as code in CI](https://tracix.io/docs/api/ci): Call the detection endpoints from a pipeline

- [Monitors and templates](https://tracix.io/docs/alerting/monitors): How alerts and episodes work
