Skip to main content

Projects and nested resources

/platform/projects/* group — projects and every nested resource (workspaces, cases, reviews, versions, members, audit, quality, impact).

Every endpoint requires an organization context and the user/key must have permission on the specific project — accessing another organization's project returns 404.

Summary table (per block)

Dashboard and CRUD

MethodPathPermission
GET/platform/dashboardproject:view
GET/platform/projectsproject:view
POST/platform/projectsproject:create
GET/platform/projects/{project_id}project:view
PATCH/platform/projects/{project_id}project:edit
GET/platform/projects/{project_id}/overviewproject:view
GET/platform/projects/{project_id}/reportproject:view
POST/platform/projects/{project_id}/impact/{workspace_id}project:view

Content (project rules and decoders)

MethodPathPermission
GET/platform/projects/{project_id}/rulesproject:view
GET/platform/projects/{project_id}/decodersproject:view

Quality

MethodPathPermission
GET/platform/projects/{project_id}/quality-gateproject:view
PATCH/platform/projects/{project_id}/quality-gateproject:edit

Test cases

MethodPathPermission
GET/platform/projects/{project_id}/casesproject:view
POST/platform/projects/{project_id}/casescase:edit
PATCH/platform/projects/{project_id}/cases/{case_id}case:edit
DELETE/platform/projects/{project_id}/cases/{case_id}case:edit
POST/platform/projects/{project_id}/cases/runcase:run
POST/platform/projects/{project_id}/cases/{case_id}/runcase:run

Workspaces (editable snapshots)

MethodPathPermission
GET/platform/projects/{project_id}/workspacesproject:view
POST/platform/projects/{project_id}/workspacesworkspace:create
GET/platform/projects/{project_id}/workspaces/{workspace_id}project:view
PATCH/platform/projects/{project_id}/workspaces/{workspace_id}workspace:create

Reviews

MethodPathPermission
GET/platform/projects/{project_id}/reviewsproject:view
POST/platform/projects/{project_id}/reviewsreview:create
PATCH/platform/projects/{project_id}/reviews/{review_id}review:approve
GET/platform/projects/{project_id}/reviews/{review_id}/commentsproject:view
POST/platform/projects/{project_id}/reviews/{review_id}/commentsreview:comment

Versions

MethodPathPermission
GET/platform/projects/{project_id}/versionsproject:view
POST/platform/projects/{project_id}/versionsversion:create
POST/platform/projects/{project_id}/versions/{version_id}/publishversion:publish
POST/platform/projects/{project_id}/versions/{version_id}/restoreversion:publish

Members

MethodPathPermission
GET/platform/projects/{project_id}/membersproject:view
POST/platform/projects/{project_id}/membersmember:manage
POST/platform/projects/{project_id}/members/assignmember:manage

Audit

MethodPathPermission
GET/platform/auditaudit:view
GET/platform/projects/{project_id}/auditaudit:view

Git integrations

MethodPathPermission
GET/platform/projects/{project_id}/git-bindingsproject:view

More Git bindings detail in Organization endpoints.


Projects

GET /platform/projects

Lists projects of the active organization.

Response 200: array of ProjectRecord.

[
{
"id": "prj_01H…",
"slug": "edge-rules",
"name": "Edge rules",
"description": "Edge team rules",
"decoders_xml": "<decoders>…</decoders>",
"rules_xml": "<group name=\"custom\">…</group>",
"default_log_format": "syslog",
"include_disk_defaults": true,
"include_disk_custom": true,
"active_baseline_id": "base_01H…",
"active_baseline_name": "baseline-4.10",
"case_count": 12,
"workspace_count": 3,
"created_at": "2026-01-15T10:00:00Z",
"updated_at": "2026-04-24T14:00:00Z"
}
]

POST /platform/projects

Creates a new project.

Request body (ProjectCreateRequest):

{
"name": "Edge rules",
"description": "Custom rules for the edge team",
"decoders_xml": "<decoders>\n</decoders>",
"rules_xml": "<group name=\"custom\">\n</group>",
"default_log_format": "syslog",
"include_disk_defaults": true,
"include_disk_custom": true,
"active_baseline_id": null
}

Validations:

  • name — 1–120 chars.
  • description — 0–2000 chars.
  • decoders_xml, rules_xml — up to 2 MB each.
  • default_log_formatauto, syslog, json, eventchannel, plain.

Response 200: ProjectRecord.

GET /platform/projects/{project_id}

Fetches a specific project.

Response 200: ProjectRecord.

PATCH /platform/projects/{project_id}

Partial update. Send only the fields to change.

Request body (ProjectUpdateRequest) — all fields optional.

Response 200: ProjectRecord.

GET /platform/projects/{project_id}/overview

Consolidated panel: quality score, policy, semantic counts, versions and recent audit.

Response 200 (ProjectOverviewResponse):

{
"project": { "...": "ProjectRecord" },
"quality_score": 87,
"quality_gate_policy": { "...": "QualityGatePolicyRecord" },
"quality_gate_result": { "passed": true, "...": "..." },
"semantic_counts": {
"total_rules": 120, "total_decoders": 45,
"root_decoders": 12, "terminal_decoders": 33,
"correlation_rules": 8, "mitre_tagged_rules": 45,
"...": "..."
},
"recent_versions": [ "ProjectVersionRecord[]" ],
"recent_audit": [ "AuditEventRecord[]" ],
"diagnostics_by_severity": {"error": 0, "warning": 3, "info": 12},
"diagnostics_by_code": {"rule_overlap": 2, "decoder_unused": 1}
}

GET /platform/projects/{project_id}/report

Full report for export: validation + regression + scores.

Response 200: ProjectReportResponse.

POST /platform/projects/{project_id}/impact/{workspace_id}

Compares the current project state against a workspace's content (publish candidate). Returns per-case diffs.

Response 200 (ProjectImpactResponse):

{
"project": { "...": "ProjectRecord" },
"workspace": { "...": "WorkspaceRecord" },
"current_quality_score": 85,
"candidate_quality_score": 92,
"current_diagnostics_by_severity": {"error": 2, "warning": 5},
"candidate_diagnostics_by_severity": {"error": 0, "warning": 3},
"changed_cases": 4,
"improved_cases": 3,
"regressed_cases": 1,
"unchanged_cases": 15,
"cases": [
{
"case_id": "case_...",
"case_name": "SSH brute force",
"before_passed": false,
"after_passed": true,
"change_type": "improved",
"...": "..."
}
]
}

Content

GET /platform/projects/{project_id}/rules

Lists the project's rules (with metadata).

Response 200: array of RuleCatalogItem:

{
"rule_id": "100010",
"level": 5,
"description": "Suspicious login attempt",
"groups": ["authentication_failed", "local"],
"decoded_as": "sshd",
"source_file": "local_rules.xml",
"source_line": 15,
"dependency_count": 0,
"dependent_rule_count": 2,
"field_matcher_count": 3,
"uses_frequency": false,
"uses_correlation": false,
"noalert": false,
"mitre_ids": ["T1110"]
}

GET /platform/projects/{project_id}/decoders

Lists the project's decoders.

Response 200: array of DecoderCatalogItem:

{
"name": "sshd-custom",
"parent": "sshd",
"source_file": "local_decoders.xml",
"source_line": 8,
"order": ["srcip", "dstuser"],
"child_count": 0,
"referenced_rule_count": 4,
"reachable_from_rule": true,
"has_regex": true,
"chain_depth": 2,
"fts_fields": ["dstuser"]
}

Quality

GET /platform/projects/{project_id}/quality-gate

Returns the current policy: score thresholds, max diagnostics per severity, etc.

Response 200: QualityGatePolicyRecord.

PATCH /platform/projects/{project_id}/quality-gate

Updates the policy. Partial request with thresholds.

Response 200: QualityGatePolicyRecord.

Test cases

A case = event + expectation. Running it verifies whether the current ruleset behavior matches what's expected.

POST /platform/projects/{project_id}/cases

Creates a case.

Request body (CaseCreateRequest):

{
"name": "SSH brute force",
"description": "Failed password SSH must match rule 5716",
"event_text": "Jan 10 12:00:01 web01 sshd[1234]: Failed password for root",
"log_format": "syslog",
"location": "/var/log/auth.log",
"session_id": "ssh-correlation",
"expected_decoder": "sshd",
"expected_rule": "5716",
"expected_rule_ids": ["5716"],
"expected_fields": { "dstuser": "root", "srcip": "10.0.0.5" }
}

Limits:

  • event_text — up to 8 MB.
  • expected_fields — up to 100 entries (key ≤ 255, value ≤ 1024).
  • expected_rule_ids — up to 200 entries.

Response 200: CaseRecord.

PATCH /platform/projects/{project_id}/cases/{case_id}

Partial update. Optional fields.

DELETE /platform/projects/{project_id}/cases/{case_id}

Removes the case. Response 204 No Content.

POST /platform/projects/{project_id}/cases/run

Runs every case in the project.

Response 200 (CaseBatchRunResponse):

{
"total_cases": 20,
"passed_cases": 18,
"failed_cases": 2,
"results": [
{
"case": { "...": "CaseRecord" },
"passed": true,
"checks": [
{ "label": "Matched expected rule", "expected": "5716", "actual": "5716", "passed": true }
],
"analysis": { "...": "LogtestResponse" }
}
]
}

Emits webhooks: regression.completed, regression.passed or regression.failed.

POST /platform/projects/{project_id}/cases/{case_id}/run

Runs a single case.

Response 200: CaseRunResponse.

Workspaces

Workspace = isolated editing area with its own ruleset. Publishing turns it into a version.

GET /platform/projects/{project_id}/workspaces

Lists open workspaces.

Response 200: array of WorkspaceRecord.

POST /platform/projects/{project_id}/workspaces

Creates a workspace.

Request body (WorkspaceCreateRequest):

{
"name": "Refactor sshd",
"description": "Rework brute-force rules",
"decoders_xml": "<decoders>…</decoders>",
"rules_xml": "<group name=\"custom\">…</group>",
"event_text": "sample event",
"log_format": "syslog",
"session_id": null,
"include_disk_defaults": true,
"include_disk_custom": true
}

Response 200: WorkspaceRecord.

PATCH /platform/projects/{project_id}/workspaces/{workspace_id}

Updates content. Optionally accepts source_files[] when the workspace originates from Git (preserves per-file provenance).

Response 200: WorkspaceRecord.

Reviews

POST /platform/projects/{project_id}/reviews

Opens a review over a workspace.

Request body (ReviewCreateRequest): { title, workspace_id, reviewer_ids, notes }.

Response 200: ReviewRecord.

PATCH /platform/projects/{project_id}/reviews/{review_id}

Updates status (approved, changes_requested, rejected, closed). Permission: review:approve.

POST /platform/projects/{project_id}/reviews/{review_id}/comments

Adds a comment on the review.

Request body (ReviewCommentCreateRequest): { body: string }.

Webhooks emitted: review.created, review.approved, review.changes_requested, review.rejected.

Versions

Version = published, immutable snapshot. Typical cycle: create (draft) → validate (quality gate) → publish.

POST /platform/projects/{project_id}/versions

Creates a version.

Request body (ProjectVersionCreateRequest):

{
"name": "v2026.04",
"notes": "Fixes sshd brute force",
"source_workspace_id": "ws_01H…",
"source_review_id": "rev_01H…"
}

Response 200: ProjectVersionRecord with status="draft".

POST /platform/projects/{project_id}/versions/{version_id}/publish

Publishes the version. The server runs the quality gate and only promotes if it passes.

Response 200 (ProjectVersionPublishResponse):

{
"version": { "...": "ProjectVersionRecord" },
"published": true,
"quality_gate_result": {
"passed": true,
"failed_rules": [],
"...": "..."
}
}

Webhooks emitted: version.created, version.published, or publish.failed.

POST /platform/projects/{project_id}/versions/{version_id}/restore

Restores this version's content back to the active project.

Response 200: ProjectRecord.

Project members

POST /platform/projects/{project_id}/members

Creates a new user already attached to the project (analogous to /auth/organizations/{id}/members, but also adds the project link).

POST /platform/projects/{project_id}/members/assign

Links an existing organization user to the project.

Request body (ProjectMemberAssignRequest):

{ "user_id": "usr_...", "role": "engineer" }

Audit

GET /platform/audit

All audited events of the active organization.

Response 200: array of AuditEventRecord. Each event carries actor_*, action, resource_*, diff, created_at.

GET /platform/projects/{project_id}/audit

Same, filtered by project.

Shared structures

CaseRecord

{
"id": "string",
"project_id": "string",
"name": "string",
"description": "string",
"event_text": "string",
"log_format": "auto|syslog|json|eventchannel|plain",
"location": "string|null",
"session_id": "string",
"expected_decoder": "string|null",
"expected_rule": "string|null",
"expected_fields": {"key": "value"},
"expected_rule_ids": ["string"],
"created_at": "ISO 8601",
"updated_at": "ISO 8601"
}

WorkspaceRecord

Includes editable content + Git provenance (when applicable):

{
"id": "string", "project_id": "string",
"name": "string", "description": "string",
"decoders_xml": "…", "rules_xml": "…",
"event_text": "string", "log_format": "…",
"session_id": "string",
"include_disk_defaults": true, "include_disk_custom": true,
"source_files": [
{ "path": "rules/local.xml", "kind": "rules", "xml": "…", "sha": "…", "rule_ids": ["100010"], "decoder_names": [] }
],
"git_binding_id": "string|null",
"git_repo": "string|null",
"git_branch": "string|null",
"git_commit_sha": "string|null",
"created_at": "…", "updated_at": "…"
}

ProjectVersionRecord

{
"id": "string", "project_id": "string",
"source_workspace_id": "string|null",
"source_review_id": "string|null",
"name": "string", "notes": "string",
"status": "draft|published|archived",
"quality_score": 87,
"regression_passed": true,
"total_rules": 120, "total_decoders": 45,
"created_at": "…", "updated_at": "…", "published_at": "…"
}