REST API for direct HTTP usage. Pre-alpha, localhost only, no authentication.
http://localhost:8000/api/v1/
| Method | Path | Body | Status | Purpose |
|---|---|---|---|---|
| POST | /requirements |
RequirementRequest | 201 | Create requirement |
| GET | /requirements |
— | 200 | List requirements (paginated, filterable) |
| GET | /requirements/{id} |
— | 200 | Get requirement by UUID |
| GET | /requirements/uid/{uid} |
— | 200 | Get requirement by UID |
| PUT | /requirements/{id} |
UpdateRequirementRequest | 200 | Update requirement (partial) |
| POST | /requirements/{id}/transition |
{ "status": "ACTIVE" } |
200 | Transition status |
| POST | /requirements/bulk/transition |
BulkStatusTransitionRequest | 200 | Bulk transition status |
| POST | /requirements/{id}/clone |
CloneRequirementRequest | 201 | Clone requirement |
| POST | /requirements/{id}/archive |
— | 200 | Archive requirement |
| Method | Path | Body | Status | Purpose |
|---|---|---|---|---|
| POST | /requirements/{id}/relations |
RelationRequest | 201 | Create relation |
| GET | /requirements/{id}/relations |
— | 200 | List relations |
| DELETE | /requirements/{id}/relations/{relationId} |
— | 204 | Delete relation |
| Method | Path | Body | Status | Purpose |
|---|---|---|---|---|
| POST | /requirements/{id}/traceability |
TraceabilityLinkRequest | 201 | Create traceability link |
| GET | /requirements/{id}/traceability |
— | 200 | List traceability links |
| GET | /requirements/traceability/by-artifact |
— | 200 | Reverse lookup: find links by artifact |
| DELETE | /requirements/{id}/traceability/{linkId} |
— | 204 | Delete traceability link |
GET /requirements/traceability/by-artifact accepts query parameters:
| Parameter | Type | Description |
|---|---|---|
artifactType |
enum | GITHUB_ISSUE, PULL_REQUEST, CODE_FILE, ADR, CONFIG, POLICY, TEST, SPEC, PROOF, DOCUMENTATION, RISK_SCENARIO, CONTROL |
artifactIdentifier |
string | Artifact identifier (e.g. repo-relative path, issue number, ADR UID) |
| Method | Path | Body | Status | Purpose |
|---|---|---|---|---|
| GET | /requirements/{id}/history |
— | 200 | Requirement revision history |
| GET | /requirements/{id}/relations/{relationId}/history |
— | 200 | Relation revision history |
| GET | /requirements/{id}/traceability/{linkId}/history |
— | 200 | Traceability link revision history |
| GET | /requirements/{id}/timeline |
— | 200 | Unified audit timeline |
GET /requirements/{id}/timeline accepts query parameters:
| Parameter | Type | Description |
|---|---|---|
changeCategory |
enum | REQUIREMENT, RELATION, TRACEABILITY_LINK |
from |
ISO-8601 instant | Start of date range |
to |
ISO-8601 instant | End of date range |
limit |
integer | Max entries to return (default 100) |
offset |
integer | Number of entries to skip (default 0) |
TimelineEntryResponse:
{
"revisionNumber": 3,
"revisionType": "MOD",
"timestamp": "2026-03-21T04:00:00Z",
"actor": "user@example.com",
"changeCategory": "REQUIREMENT",
"entityId": "uuid",
"snapshot": { "title": "New Title", "status": "ACTIVE", "..." : "..." },
"changes": { "title": { "oldValue": "Old Title", "newValue": "New Title" } }
}| Method | Path | Body | Status | Purpose |
|---|---|---|---|---|
| GET | /analysis/cycles |
— | 200 | Detect dependency cycles |
| GET | /analysis/orphans |
— | 200 | Find orphan requirements |
| GET | /analysis/coverage-gaps?linkType=X |
— | 200 | Find coverage gaps by link type |
| GET | /analysis/impact/{id} |
— | 200 | Transitive impact analysis |
| GET | /analysis/cross-wave |
— | 200 | Cross-wave dependency violations |
| GET | /analysis/consistency-violations |
— | 200 | Detect consistency violations |
| GET | /analysis/completeness |
— | 200 | Analyze completeness |
| GET | /analysis/work-order |
— | 200 | Topological work order |
| GET | /analysis/dashboard-stats |
— | 200 | Aggregate project health stats |
| GET | /analysis/semantic-similarity |
— | 200 | Find semantically similar requirement pairs |
| POST | /analysis/sweep |
— | 200 | Run analysis sweep on one project |
| POST | /analysis/sweep/all |
— | 200 | Run analysis sweep on all projects |
CycleResponse (GET /analysis/cycles):
[
{
"members": ["REQ-A", "REQ-B", "REQ-C", "REQ-A"],
"edges": [
{ "sourceUid": "REQ-A", "targetUid": "REQ-B", "relationType": "DEPENDS_ON" },
{ "sourceUid": "REQ-B", "targetUid": "REQ-C", "relationType": "DEPENDS_ON" },
{ "sourceUid": "REQ-C", "targetUid": "REQ-A", "relationType": "PARENT" }
]
}
]Each cycle lists the member UIDs (closing back to the start) and the edges that form it, including the relation type between each consecutive pair.
GET /analysis/semantic-similarity accepts query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
project |
string | auto-resolved | Project identifier |
threshold |
double | 0.85 | Minimum similarity score (0–1) |
SimilarityResultResponse:
{
"totalRequirements": 50,
"embeddedCount": 48,
"pairsAnalyzed": 1128,
"threshold": 0.85,
"pairs": [
{
"uid1": "REQ-012",
"title1": "User authentication via SSO",
"uid2": "REQ-037",
"title2": "Single sign-on login support",
"score": 0.93
}
]
}| Method | Path | Body | Status | Purpose |
|---|---|---|---|---|
| POST | /embeddings/{requirementId} |
— | 200 | Embed a single requirement |
| GET | /embeddings/{requirementId}/status |
— | 200 | Get embedding status |
| POST | /embeddings/batch?project=&force=false |
— | 200 | Batch embed all requirements in a project |
| DELETE | /embeddings/{requirementId} |
— | 204 | Delete embedding |
Requires GC_EMBEDDING_PROVIDER=openai and GC_EMBEDDING_API_KEY to be set.
When no provider is configured, endpoints return provider_unavailable status
(graceful degradation).
EmbeddingStatusResponse (GET /embeddings/{id}/status):
{
"requirementId": "uuid",
"hasEmbedding": true,
"isStale": false,
"modelMismatch": false,
"currentModelId": "text-embedding-3-small",
"embeddingModelId": "text-embedding-3-small",
"embeddedAt": "2026-03-22T03:00:00Z"
}| Method | Path | Body | Status | Purpose |
|---|---|---|---|---|
| POST | /baselines?project= |
BaselineRequest | 201 | Create baseline |
| GET | /baselines?project= |
— | 200 | List baselines |
| GET | /baselines/{id} |
— | 200 | Get baseline |
| GET | /baselines/{id}/snapshot |
— | 200 | Requirement snapshot at baseline |
| GET | /baselines/{id}/compare/{otherId} |
— | 200 | Compare two baselines |
| DELETE | /baselines/{id} |
— | 204 | Delete baseline |
BaselineRequest:
{
"name": "v1.0",
"description": "First release baseline"
}BaselineComparisonResponse (GET /baselines/{id}/compare/{otherId}):
{
"baselineId": "uuid",
"baselineName": "v1.0",
"otherBaselineId": "uuid",
"otherBaselineName": "v2.0",
"addedCount": 2,
"removedCount": 0,
"modifiedCount": 1,
"added": [...],
"removed": [...],
"modified": [{ "requirementId": "uuid", "uid": "REQ-001", "before": {...}, "after": {...} }]
}| Method | Path | Body | Status | Purpose |
|---|---|---|---|---|
| PUT | /documents/{id}/grammar |
Grammar JSON | 200 | Set/replace grammar |
| GET | /documents/{id}/grammar |
— | 200 | Get grammar |
| DELETE | /documents/{id}/grammar |
— | 204 | Remove grammar |
Grammar JSON:
{
"fields": [
{"name": "acceptance_criteria", "type": "STRING", "required": false},
{"name": "risk_level", "type": "ENUM", "required": true, "enumValues": ["LOW", "MEDIUM", "HIGH"]}
],
"allowedRequirementTypes": ["FUNCTIONAL", "NON_FUNCTIONAL"],
"allowedRelationTypes": ["PARENT", "DEPENDS_ON", "REFINES"]
}Field types: STRING, INTEGER, BOOLEAN, ENUM. Declarative metadata — no runtime enforcement.
| Method | Path | Body | Status | Purpose |
|---|---|---|---|---|
| GET | /documents/{id}/reading-order |
— | 200 | Full document in reading order |
Returns the document with all sections nested, each containing its content items (requirement references and text blocks) in authored sequence.
| Method | Path | Body | Status | Purpose |
|---|---|---|---|---|
| POST | /sections/{sectionId}/content |
SectionContentRequest | 201 | Add content item |
| GET | /sections/{sectionId}/content |
— | 200 | List content in order |
| PUT | /sections/content/{id} |
UpdateSectionContentRequest | 200 | Update content item |
| DELETE | /sections/content/{id} |
— | 204 | Delete content item |
SectionContentRequest:
{
"contentType": "REQUIREMENT",
"requirementId": "uuid",
"sortOrder": 0
}or for text blocks:
{
"contentType": "TEXT_BLOCK",
"textContent": "This section describes...",
"sortOrder": 1
}| Method | Path | Body | Status | Purpose |
|---|---|---|---|---|
| POST | /documents/{documentId}/sections |
SectionRequest | 201 | Create section |
| GET | /documents/{documentId}/sections |
— | 200 | List sections (flat) |
| GET | /documents/{documentId}/sections/tree |
— | 200 | Get section tree (nested) |
| GET | /sections/{id} |
— | 200 | Get section |
| PUT | /sections/{id} |
UpdateSectionRequest | 200 | Update section |
| DELETE | /sections/{id} |
— | 204 | Delete section (cascades children) |
SectionRequest:
{
"parentId": null,
"title": "Chapter 1: Introduction",
"description": "Overview section",
"sortOrder": 0
}Sections support arbitrary nesting — set parentId to a section UUID to create a child.
The tree endpoint returns a nested JSON structure with children arrays.
| Method | Path | Body | Status | Purpose |
|---|---|---|---|---|
| POST | /documents?project= |
DocumentRequest | 201 | Create document |
| GET | /documents?project= |
— | 200 | List documents |
| GET | /documents/{id} |
— | 200 | Get document |
| PUT | /documents/{id} |
UpdateDocumentRequest | 200 | Update document |
| DELETE | /documents/{id} |
— | 204 | Delete document |
DocumentRequest:
{
"title": "System Requirements Specification",
"version": "1.0.0",
"description": "Top-level SRS document"
}| Method | Path | Body | Status | Purpose |
|---|---|---|---|---|
| POST | /adrs?project= |
AdrRequest | 201 | Create ADR |
| GET | /adrs?project= |
— | 200 | List ADRs |
| GET | /adrs/{id} |
— | 200 | Get ADR by UUID |
| GET | /adrs/uid/{uid}?project= |
— | 200 | Get ADR by UID |
| PUT | /adrs/{id} |
UpdateAdrRequest | 200 | Update ADR (partial) |
| DELETE | /adrs/{id} |
— | 204 | Delete ADR |
| PUT | /adrs/{id}/status |
{ "status": "ACCEPTED" } |
200 | Transition status |
| GET | /adrs/{id}/requirements |
— | 200 | Get linked requirements (reverse traceability) |
AdrRequest:
{
"uid": "ADR-018",
"title": "AWS EC2 Deployment",
"decisionDate": "2026-03-15",
"context": "We need a deployment target for the application",
"decision": "Deploy to AWS EC2 with Docker",
"consequences": "Simple, cost-effective, but single-instance"
}Status transitions: PROPOSED → ACCEPTED → DEPRECATED | SUPERSEDED
| Method | Path | Body | Status | Purpose |
|---|---|---|---|---|
| POST | /assets?project= |
AssetRequest | 201 | Create asset |
| GET | /assets?project=&type= |
— | 200 | List assets (optional type filter) |
| GET | /assets/{id} |
— | 200 | Get asset by UUID |
| GET | /assets/uid/{uid}?project= |
— | 200 | Get asset by UID |
| PUT | /assets/{id} |
UpdateAssetRequest | 200 | Update asset (partial) |
| DELETE | /assets/{id} |
— | 204 | Delete asset (cascade deletes relations) |
| POST | /assets/{id}/archive |
— | 200 | Archive (soft-delete) asset |
AssetRequest:
{
"uid": "ASSET-001",
"name": "Production Database",
"description": "Primary PostgreSQL instance",
"assetType": "DATABASE"
}Asset types: APPLICATION, SERVICE, SYSTEM, DATABASE, NETWORK, HOST, CONTAINER, IDENTITY, DATA_STORE, ENDPOINT, INTEGRATION, WORKLOAD, THIRD_PARTY, BOUNDARY, OTHER
| Method | Path | Body | Status | Purpose |
|---|---|---|---|---|
| POST | /assets/{id}/relations |
AssetRelationRequest | 201 | Create typed relation |
| PUT | /assets/{id}/relations/{relationId} |
UpdateAssetRelationRequest | 200 | Update relation metadata |
| GET | /assets/{id}/relations |
— | 200 | List relations (incoming + outgoing) |
| DELETE | /assets/{id}/relations/{relationId} |
— | 204 | Delete relation |
AssetRelationRequest:
{
"targetId": "uuid",
"relationType": "DEPENDS_ON",
"description": "Observed runtime dependency",
"sourceSystem": "AWS_CONFIG",
"externalSourceId": "cfg-123",
"collectedAt": "2026-04-01T12:00:00Z",
"confidence": "0.80"
}UpdateAssetRelationRequest:
{
"description": "Refined runtime dependency",
"sourceSystem": "CMDB",
"externalSourceId": "cmdb-789",
"collectedAt": "2026-04-02T12:00:00Z",
"confidence": "0.95"
}AssetRelationResponse fields: id, sourceId, sourceUid, targetId, targetUid, relationType, description, sourceSystem, externalSourceId, collectedAt, confidence, createdAt, updatedAt
Relation types: CONTAINS, DEPENDS_ON, COMMUNICATES_WITH, TRUST_BOUNDARY, SUPPORTS, ACCESSES, DATA_FLOW
| Method | Path | Body | Status | Purpose |
|---|---|---|---|---|
| POST | /assets/{id}/links |
AssetLinkRequest | 201 | Link asset to a requirement, control, or other entity |
| GET | /assets/{id}/links?target_type= |
— | 200 | List links (optional target type filter) |
| DELETE | /assets/{id}/links/{linkId} |
— | 204 | Delete link |
| GET | /assets/links/by-target?target_type=&target_identifier=&project= |
— | 200 | Reverse lookup: find assets linked to a target |
AssetLinkRequest:
{
"targetType": "REQUIREMENT",
"targetIdentifier": "GC-M010",
"linkType": "IMPLEMENTS",
"targetUrl": "https://example.com/req/GC-M010",
"targetTitle": "Operational Asset Entity"
}Target types: REQUIREMENT, CONTROL, RISK_SCENARIO, THREAT_MODEL_ENTRY, FINDING, EVIDENCE, AUDIT, EXTERNAL
Link types: IMPLEMENTS, MITIGATES, SUBJECT_OF, EVIDENCED_BY, GOVERNED_BY, DEPENDS_ON, ASSOCIATED
| Method | Path | Body | Status | Purpose |
|---|---|---|---|---|
| GET | /assets/topology/cycles?project= |
— | 200 | Detect cycles in asset graph |
| GET | /assets/{id}/topology/impact |
— | 200 | Multi-hop impact analysis |
| POST | /assets/topology/subgraph?project= |
SubgraphRequest | 200 | Extract connected subgraph |
SubgraphRequest:
{
"rootUids": ["ASSET-001", "ASSET-002"]
}AssetSubgraphResponse:
{
"assets": [{ "id": "uuid", "uid": "ASSET-001", "name": "...", "..." : "..." }],
"relations": [{ "id": "uuid", "sourceUid": "ASSET-001", "targetUid": "ASSET-002", "relationType": "DEPENDS_ON" }]
}| Method | Path | Body | Status | Purpose |
|---|---|---|---|---|
| POST | /quality-gates?project= |
QualityGateRequest | 201 | Create quality gate |
| GET | /quality-gates?project= |
— | 200 | List quality gates |
| GET | /quality-gates/{id} |
— | 200 | Get quality gate |
| PUT | /quality-gates/{id} |
UpdateQualityGateRequest | 200 | Update quality gate |
| DELETE | /quality-gates/{id} |
— | 204 | Delete quality gate |
| POST | /quality-gates/evaluate?project= |
— | 200 | Evaluate all enabled gates (CI/CD) |
QualityGateRequest:
{
"name": "Test Coverage Gate",
"description": "Minimum 80% of ACTIVE requirements must have TESTS link",
"metricType": "COVERAGE",
"metricParam": "TESTS",
"scopeStatus": "ACTIVE",
"operator": "GTE",
"threshold": 80.0
}metricType:COVERAGE(% with link type),ORPHAN_COUNT,COMPLETENESS(issue count)metricParam: Required forCOVERAGE— a LinkType (IMPLEMENTS,TESTS,DOCUMENTS,CONSTRAINS,VERIFIES)scopeStatus: Filter requirements by status. Omit to check all non-archivedoperator:GTE(>=),LTE(<=),EQ(==),GT(>),LT(<)
QualityGateEvaluationResponse (POST /quality-gates/evaluate):
{
"projectIdentifier": "ground-control",
"timestamp": "2026-03-24T06:00:00Z",
"passed": false,
"totalGates": 2,
"passedCount": 1,
"failedCount": 1,
"gates": [
{
"gateId": "uuid",
"gateName": "Test Coverage Gate",
"metricType": "COVERAGE",
"metricParam": "TESTS",
"scopeStatus": "ACTIVE",
"operator": "GTE",
"threshold": 80.0,
"actualValue": 65.0,
"passed": false
}
]
}| Method | Path | Body | Status | Purpose |
|---|---|---|---|---|
| POST | /admin/graph/materialize |
— | 200 | Materialize graph (AGE) |
| GET | /graph/ancestors/{uid}?depth=N |
— | 200 | Ancestor UIDs |
| GET | /graph/descendants/{uid}?depth=N |
— | 200 | Descendant UIDs |
| GET | /graph/visualization?entityTypes=X,Y |
— | 200 | Full graph (filterable by entity type) |
| GET | /graph/subgraph?roots=X&entityTypes=Y |
— | 200 | Subgraph (filterable by entity type) |
| GET | /graph/paths?source=X&target=Y |
— | 200 | All paths between two UIDs (with edges) |
entityTypes is an optional comma-separated list (e.g. REQUIREMENT). When omitted, all entity types are returned. Each node includes an entityType field.
Path response shape:
[
{
"nodes": ["REQ-A", "REQ-B", "REQ-C"],
"edges": [
{ "sourceUid": "REQ-A", "targetUid": "REQ-B", "relationType": "DEPENDS_ON" },
{ "sourceUid": "REQ-B", "targetUid": "REQ-C", "relationType": "PARENT" }
]
}
]| Method | Path | Body | Status | Purpose |
|---|---|---|---|---|
| POST | /admin/github/issues |
GitHubIssueRequest | 201 | Create issue from requirement |
GitHubIssueRequest:
{
"requirementUid": "GC-A001",
"repo": "owner/repo",
"extraBody": "Additional markdown (optional)",
"labels": ["enhancement"]
}GitHubIssueResponse:
{
"issueUrl": "https://github.com/owner/repo/issues/42",
"issueNumber": 42,
"traceabilityLinkId": "uuid",
"warning": null
}| Method | Path | Body | Status | Purpose |
|---|---|---|---|---|
| GET | /export/requirements?project=&format=csv |
— | 200 | Export requirements as CSV, Excel, or PDF |
| POST | /export/sweep?project=&format=csv |
— | 200 | Run sweep and export as CSV, Excel, or PDF |
| GET | /export/document/{documentId}?format=sdoc |
— | 200 | Export document (sdoc, html, pdf, or reqif) |
The format query parameter accepts csv (default), xlsx, or pdf. Responses include
Content-Disposition: attachment headers with a generated filename.
Content types by format:
csv—text/csvxlsx—application/vnd.openxmlformats-officedocument.spreadsheetml.sheetpdf—application/pdf
Requirements export includes: UID, title, statement, rationale, type, priority, status, wave, traceability links, timestamps. Excel format adds a second "Traceability" sheet with the full link matrix.
Sweep export includes: summary, cycles, orphans, coverage gaps, cross-wave violations, consistency violations, completeness, and quality gate results. Excel format uses one sheet per analysis category.
| Method | Path | Body | Status | Purpose |
|---|---|---|---|---|
| POST | /admin/import/strictdoc |
multipart/form-data | 200 | Import .sdoc file |
| POST | /admin/import/reqif |
multipart/form-data | 200 | Import .reqif file |
| POST | /admin/sync/github?owner=X&repo=Y |
— | 200 | Sync GitHub issues |
| POST | /admin/sync/github/prs?owner=X&repo=Y |
— | 200 | Sync GitHub pull requests |
StrictDoc import creates requirements, relations, traceability links, and preserves the
document structure (document, sections, text blocks). The response includes all counters:
requirementsParsed, requirementsCreated, requirementsUpdated, relationsCreated,
relationsSkipped, traceabilityLinksCreated, traceabilityLinksSkipped,
documentsCreated, sectionsCreated, sectionContentsCreated, errors.
| Method | Path | Body | Status | Purpose |
|---|---|---|---|---|
| POST | /verification-results |
VerificationResultRequest | 201 | Create verification result |
| GET | /verification-results |
— | 200 | List verification results |
| GET | /verification-results/{id} |
— | 200 | Get verification result by UUID |
| PUT | /verification-results/{id} |
UpdateVerificationResultRequest | 200 | Update verification result |
| DELETE | /verification-results/{id} |
— | 204 | Delete verification result |
All endpoints accept an optional project query parameter.
Filters on GET list:
requirement_id(UUID) — filter by requirementprover(string) — filter by verifier tool identifierresult(enum) — PROVEN, REFUTED, TIMEOUT, UNKNOWN, ERROR
VerificationResultRequest fields: prover (required), result (required),
assuranceLevel (required, L0-L3), verifiedAt (required, ISO 8601), targetId
(optional, traceability link UUID), requirementId (optional), property (optional),
evidence (optional, JSON object), expiresAt (optional, ISO 8601).
| Method | Path | Body | Status | Purpose |
|---|---|---|---|---|
| GET | /plugins |
— | 200 | List all registered plugins |
| GET | /plugins/{name} |
— | 200 | Get plugin by name |
| POST | /plugins |
RegisterPluginRequest | 201 | Register a dynamic plugin |
| DELETE | /plugins/{name} |
— | 204 | Unregister a dynamic plugin |
All endpoints accept an optional project query parameter.
Filters on GET list:
type(enum) — PACK_HANDLER, REGISTRY_BACKEND, VALIDATOR, POLICY_HOOK, VERIFIER, EMBEDDING_PROVIDER, GRAPH_CONTRIBUTOR, CUSTOMcapability(string) — filter by capability tagproject(string) — filter dynamic plugins by project
RegisterPluginRequest fields: name (required, max 100), version (required, max 50),
type (required, PluginType enum), description (optional), capabilities (optional, string set),
metadata (optional, JSON object).
| Method | Path | Body | Status | Purpose |
|---|---|---|---|---|
| GET | /control-packs |
— | 200 | List installed packs |
| GET | /control-packs/{packId} |
— | 200 | Get pack by identifier |
| PUT | /control-packs/{packId}/deprecate |
— | 200 | Deprecate a pack |
| DELETE | /control-packs/{packId} |
— | 204 | Remove a pack |
| GET | /control-packs/{packId}/entries |
— | 200 | List pack entries |
| GET | /control-packs/{packId}/entries/{entryUid} |
— | 200 | Get a pack entry |
| POST | /control-packs/{packId}/entries/{entryUid}/overrides |
CreateControlPackOverrideRequest | 201 | Create field override |
| GET | /control-packs/{packId}/entries/{entryUid}/overrides |
— | 200 | List overrides |
| DELETE | /control-packs/{packId}/entries/{entryUid}/overrides/{id} |
— | 204 | Delete override |
All endpoints accept an optional project query parameter.
Control-pack installation and upgrade are registry-backed operations only. Register
or import a CONTROL_PACK in /pack-registry, then use /pack-install-records/install
or /pack-install-records/upgrade so resolution, trust evaluation, and audit recording
cannot be bypassed.
CreateControlPackOverrideRequest fields: fieldName (required — title, description, objective,
controlFunction, owner, implementationScope, or category), overrideValue (optional; title
must be non-blank), reason (optional, max 500).
Lifecycle states: INSTALLED → UPGRADED → DEPRECATED → REMOVED.
JSON. Error responses use a nested envelope:
{
"error": {
"code": "NOT_FOUND",
"message": "Requirement not found",
"detail": {}
}
}HTTP status codes: 201 (created), 200 (ok), 204 (deleted), 404 (not found), 409 (conflict), 422 (validation error).
GET /api/v1/requirements accepts query parameters:
| Parameter | Type | Description |
|---|---|---|
status |
enum | DRAFT, ACTIVE, DEPRECATED, ARCHIVED |
type |
enum | FUNCTIONAL, NON_FUNCTIONAL, CONSTRAINT, INTERFACE |
wave |
integer | Wave number |
search |
string | Free-text search in title and statement |
Archived requirements are excluded by default. Filter by status=ARCHIVED
to include them.
Standard Spring Page parameters:
| Parameter | Default | Description |
|---|---|---|
page |
0 | Page number (0-based) |
size |
20 | Page size |
sort |
— | Sort field and direction (e.g. sort=uid,asc) |
Response wraps results in a Spring Page object with content, totalElements,
totalPages, number, size.
All pack registry, trust policy, and pack install record routes require a pack
registry admin token: Authorization: Bearer <token>. Tokens and their audit
principal names are configured with
ground-control.pack-registry.security.admin-credentials. The repo-local MCP
helper forwards GROUND_CONTROL_PACK_REGISTRY_ADMIN_TOKEN when set.
| Method | Path | Body | Status | Purpose |
|---|---|---|---|---|
| POST | /pack-registry |
RegisterPackRequest | 201 | Register pack version in catalog |
| POST | /pack-registry/import |
multipart/form-data | 201 | Import and register a pack from uploaded JSON |
| GET | /pack-registry |
— | 200 | List registry entries (optional packType filter) |
| GET | /pack-registry/{packId} |
— | 200 | List versions of a pack |
| GET | /pack-registry/{packId}/{version} |
— | 200 | Get specific pack version |
| PUT | /pack-registry/{packId}/{version} |
UpdatePackRegistryEntryRequest | 200 | Update pack metadata |
| PUT | /pack-registry/{packId}/{version}/withdraw |
— | 200 | Withdraw pack version |
| DELETE | /pack-registry/{packId}/{version} |
— | 204 | Delete pack version |
| POST | /pack-registry/resolve |
ResolvePackRequest | 200 | Resolve version from registry |
| POST | /pack-registry/check-compatibility |
ResolvePackRequest | 200 | Check pack compatibility (returns boolean) |
For large catalogs, use POST /pack-registry/import instead of hand-authoring a
giant JSON request body. The endpoint accepts a multipart file part plus an
optional JSON options part. Supported formats are:
AUTO— detect OSCAL catalog JSON vs Ground Control manifest JSONOSCAL_JSON— treat the file as an OSCAL catalog and flatten controls into aCONTROL_PACKGC_MANIFEST— treat the file as a Ground Control pack manifest and register it directly
options may override pack metadata such as packId, version, publisher,
description, sourceUrl, checksum, signatureInfo, compatibility,
dependencies, provenance, registryMetadata, and
defaultControlFunction for imported control entries.
Example multipart call:
curl -X POST "http://localhost:8000/api/v1/pack-registry/import?project=ground-control" \
-H "Authorization: Bearer <token>" \
-F "file=@/path/to/catalog.json;type=application/json" \
-F 'options={"format":"OSCAL_JSON","packId":"nist-sp800-53-rev5","version":"5.1.0","publisher":"NIST"};type=application/json'| Method | Path | Body | Status | Purpose |
|---|---|---|---|---|
| POST | /trust-policies |
CreateTrustPolicyRequest | 201 | Create trust policy |
| GET | /trust-policies |
— | 200 | List trust policies |
| GET | /trust-policies/{id} |
— | 200 | Get trust policy |
| PUT | /trust-policies/{id} |
UpdateTrustPolicyRequest | 200 | Update trust policy |
| DELETE | /trust-policies/{id} |
— | 204 | Delete trust policy |
| Method | Path | Body | Status | Purpose |
|---|---|---|---|---|
| POST | /pack-install-records/install |
InstallPackRequest | 201, 422 | Install pack via registry with trust evaluation |
| POST | /pack-install-records/upgrade |
InstallPackRequest | 200, 422 | Upgrade pack via registry with trust evaluation |
| GET | /pack-install-records |
— | 200 | List install records (optional packId filter) |
| GET | /pack-install-records/{id} |
— | 200 | Get install record |
For control packs, use /pack-registry/import or /pack-registry to persist the
pack definition first, then call one of these routes with the packId and optional
version constraint.
RegisterPackRequest and UpdatePackRegistryEntryRequest accept
controlPackEntries for CONTROL_PACK artifacts. Registry-driven install and
upgrade now materialize that stored server-side content; InstallPackRequest
contains only packId and optional versionConstraint. The install record
performedBy value is derived server-side from the authenticated admin token,
not request JSON.
When a checksum is supplied, the server verifies it against the canonical
pack payload and normalizes the stored value to sha256:<hex>. Unsigned packs
may omit checksum; they still produce a computed verifiedChecksum during
trust evaluation and install recording, but they do not become
checksumVerified=true by registry round-trip alone.
signatureInfo is optional detached signature metadata with this shape:
algorithm (required, one of SHA256withRSA, SHA384withRSA,
SHA512withRSA, SHA256withECDSA, SHA384withECDSA, SHA512withECDSA,
Ed25519, or Ed448),
publicKey (required, base64 DER or PEM-encoded X.509 public key),
signature (required, base64 detached signature over the canonical pack
payload), and keyAlgorithm (optional when it can be inferred from
algorithm, otherwise required). A valid signature is cryptographic evidence
only. Trust policy must use signerTrusted, which becomes true only when the
signature public key matches a configured trusted signer under
ground-control.pack-registry.security.trusted-signers.
Install and upgrade return 422 Unprocessable Entity when the request is
accepted syntactically but the resolved pack is rejected or fails to apply.
Trust policy rules may match not only raw pack metadata, but also verified
integrity fields exposed by the server: verifiedChecksum,
checksumVerified, and signerTrusted. The signatureVerified field is
informational and is rejected in trust policy rules. Regex policy rules are also
disabled; use bounded operators EQUALS, NOT_EQUALS, CONTAINS, and
IN_LIST.
- Swagger UI:
http://localhost:8000/api/docs - OpenAPI JSON:
http://localhost:8000/api/openapi.json