Skip to content

API Endpoints Reference

Complete reference for the SCP REST API.

Base URL: http://localhost:8000 (development)

Interactive Documentation: Visit /docs (Swagger UI) or /redoc (ReDoc)


Authentication

Agent Authentication (v0.3)

Agents authenticate using API keys in the X-API-Key header:

X-API-Key: sk_your_api_key_here

API keys are generated when registering agents (see Agent Registry API).

Admin Authentication

Admin operations (bundle registry, agent registry, audit admin reads) require an X-Admin-Key header:

X-Admin-Key: admin_your_key_here

The admin key is set via ADMIN_API_KEY in the deployment environment. Requests without a valid key return 401. Requests to a deployment where the key is not configured return 503.

See Security Overview for key generation and storage guidance.


Context API (v0.3)

The primary API for agents to request governed context.

Request Context

POST /api/context
Content-Type: application/json
X-API-Key: sk_your_api_key_here

{
  "task_type": "prior_auth_check",
  "params": {
    "drug": "Keytruda",
    "diagnosis": "lung cancer"
  }
}

Response: 200 OK

{
  "success": true,
  "request_id": "ctx_abc123def456",
  "agent_id": "agent:prior-auth-prod",
  "role_id": "role:prior-auth-agent",
  "task_type": "prior_auth_check",
  "scd_ids": [
    "scd:standards:hipaa-privacy-rule",
    "scd:project:clinical-guidelines-v1.2",
    "scd:project:formulary-2024"
  ],
  "scd_content": {
    "scd:standards:hipaa-privacy-rule": { ... },
    "scd:project:clinical-guidelines-v1.2": { ... }
  },
  "timestamp": 1706288400,
  "processing_time_ms": 34
}

Error Responses:

401 Unauthorized - Invalid or missing API key

{
  "success": false,
  "error_code": "AUTH_FAILED",
  "error_message": "Invalid or expired API key"
}

403 Forbidden - Task type not allowed for this agent

{
  "success": false,
  "error_code": "INTENT_NOT_ALLOWED",
  "error_message": "Task type 'claims_adjudication' not in agent's allowed intents"
}


Context Health Check

GET /api/context/health

Response: 200 OK

{
  "status": "healthy",
  "service": "control-plane-api",
  "version": "1.0.0"
}


Agent Registry API

Register Agent

POST /api/agents
Content-Type: application/json

{
  "agent_id": "agent:prior-auth-prod",
  "name": "Prior Auth Production Agent",
  "role_id": "role:prior-auth-agent",
  "allowed_intents": [
    "prior_auth_check",
    "formulary_lookup",
    "clinical_guidelines_check"
  ],
  "status": "active"
}

Response: 201 Created

{
  "agent_id": "agent:prior-auth-prod",
  "name": "Prior Auth Production Agent",
  "role_id": "role:prior-auth-agent",
  "allowed_intents": ["prior_auth_check", "formulary_lookup", "clinical_guidelines_check"],
  "status": "active",
  "created_at": "2026-01-26T10:00:00Z"
}


List Agents

GET /api/agents?role_id=role:prior-auth-agent&status=active

Query Parameters: - role_id - Filter by role - status - Filter by status (active, inactive, suspended) - limit - Max results (default: 100) - offset - Pagination offset

Response: 200 OK

{
  "agents": [
    {
      "agent_id": "agent:prior-auth-prod",
      "name": "Prior Auth Production Agent",
      "role_id": "role:prior-auth-agent",
      "status": "active",
      "created_at": "2026-01-26T10:00:00Z"
    }
  ],
  "total": 1
}


Get Agent

GET /api/agents/{agent_id}

Response: 200 OK

{
  "agent_id": "agent:prior-auth-prod",
  "name": "Prior Auth Production Agent",
  "role_id": "role:prior-auth-agent",
  "allowed_intents": ["prior_auth_check", "formulary_lookup"],
  "status": "active",
  "created_at": "2026-01-26T10:00:00Z",
  "api_keys": [
    {
      "key_id": "key_prod_default",
      "created_at": "2026-01-26T10:00:00Z",
      "last_used_at": "2026-01-26T15:30:00Z"
    }
  ]
}


Update Agent

PUT /api/agents/{agent_id}
Content-Type: application/json

{
  "name": "Updated Agent Name",
  "allowed_intents": ["prior_auth_check", "formulary_lookup", "new_intent"],
  "status": "active"
}

Response: 200 OK


Generate API Key

POST /api/agents/{agent_id}/api-keys
Content-Type: application/json

{
  "key_id": "key_prod_v2"
}

Response: 201 Created

{
  "key_id": "key_prod_v2",
  "api_key": "sk_abc123def456...",
  "agent_id": "agent:prior-auth-prod",
  "created_at": "2026-01-26T10:00:00Z"
}

Important: The api_key is only returned once. Store it securely.


Revoke API Key

DELETE /api/agents/{agent_id}/api-keys/{key_id}

Response: 204 No Content


Add Policy Override

Add SCD overrides or restrictions for a specific agent.

POST /api/agents/{agent_id}/policy
Content-Type: application/json

{
  "overrides": ["scd:project:special-guidelines"],
  "restrictions": ["scd:project:phi-handling"]
}

Response: 200 OK


MCP Server Registry API

Governed the same way agents are (see Agent Registry API above) — MCP servers/connectors are proactively registered before they touch regulated data, with an accountable human owner (HITL). Deliberately a separate table from agents — an MCP server is a tool/connector, not an agent. Requires the X-Admin-Key header (all routes in this group carry the admin-key dependency).

Register MCP Server

POST /api/mcp-servers
Content-Type: application/json
X-Admin-Key: admin_your_key_here

{
  "name": "Propel",
  "description": "Propel MCP connector — supplier/vendor qualification lookups",
  "owner_name": "Caroline",
  "owner_verified": false,
  "created_by": "tim@ohana-consulting.com"
}

Response: 200 OK

{
  "server_id": "mcp_a1b2c3d4e5f6",
  "name": "Propel",
  "description": "Propel MCP connector — supplier/vendor qualification lookups",
  "status": "active",
  "owner_name": "Caroline",
  "owner_verified": false,
  "created_by": "tim@ohana-consulting.com",
  "created_at": "2026-07-25T10:00:00Z",
  "updated_at": "2026-07-25T10:00:00Z"
}

owner_verified is false until confirmed against a real identity provider (SSO) — same convention as agents.owner_verified. See Data Dictionary.


List MCP Servers

GET /api/mcp-servers?status=active&limit=50&offset=0
X-Admin-Key: admin_your_key_here

Query Parameters: status (filter), limit (default 50, max 100), offset.

Response: 200 OK{ "servers": [ ... ], "total": 1 }


Get / Update / Delete MCP Server

GET /api/mcp-servers/{server_id}
PUT /api/mcp-servers/{server_id}
DELETE /api/mcp-servers/{server_id}

All require X-Admin-Key. PUT accepts a partial update (any subset of name, description, status, owner_name, owner_verified).


Bundle Registry API

Create/Publish Bundle

POST /api/bundles
Content-Type: application/json

{
  "id": "bundle:my-bundle",
  "type": "concern",
  "version": "1.0.0",
  "title": "My Bundle",
  "description": "Bundle description",
  "scds": ["scd:project:my-scd"],
  "imports": [],
  "provenance": {
    "created_by": "user@example.com",
    "created_at": "2026-01-26T10:00:00Z",
    "rationale": "Initial bundle creation"
  }
}

Response: 201 Created

{
  "bundle_id": "bundle:my-bundle",
  "version": "1.0.0",
  "status": "published"
}


List Bundles

GET /api/bundles?type=concern&limit=100

Query Parameters: - type - Filter by bundle type (meta, standards, domain, concern, project) - limit - Max results (default: 100) - offset - Pagination offset

Response: 200 OK

{
  "bundles": [
    {
      "id": "bundle:my-bundle",
      "version": "1.0.0",
      "type": "concern",
      "title": "My Bundle",
      "created_at": "2026-01-26T10:00:00Z"
    }
  ],
  "total": 1
}


Get Bundle Version

GET /api/bundles/{bundle_id}/versions/{version}

Response: 200 OK

{
  "id": "bundle:acme-security",
  "type": "concern",
  "version": "1.0.0",
  "title": "Acme Security Controls",
  "description": "Security standards for Acme Health Corp",
  "scds": ["scd:project:security-controls"],
  "imports": [],
  "manifest": { ... }
}


Deactivate Bundle

DELETE /api/bundles/{bundle_id}/versions/{version}

Response: 204 No Content


Tag Management API

Tags are mutable pointers to immutable bundles.

Create Tag

POST /api/tags
Content-Type: application/json

{
  "tag": "prior-auth:latest",
  "bundle_id": "bundle:prior-auth",
  "bundle_version": "1.0.0"
}

Response: 201 Created

{
  "tag": "prior-auth:latest",
  "bundle_id": "bundle:prior-auth",
  "bundle_version": "1.0.0",
  "created_at": "2026-01-26T10:00:00Z"
}


Update Tag

Move a tag to point to a different bundle version (zero-downtime update).

PUT /api/tags/{tag}
Content-Type: application/json

{
  "bundle_id": "bundle:prior-auth",
  "bundle_version": "1.1.0"
}

Response: 200 OK

{
  "tag": "prior-auth:latest",
  "bundle_id": "bundle:prior-auth",
  "bundle_version": "1.1.0",
  "updated_at": "2026-01-26T12:00:00Z"
}


Resolve Tag

GET /api/tags/{tag}

Response: 200 OK

{
  "tag": "prior-auth:latest",
  "bundle_id": "bundle:prior-auth",
  "bundle_version": "1.1.0",
  "resolved_bundle_ids": [
    "bundle:prior-auth:1.1.0",
    "bundle:acme-health-corp:1.0.0",
    "bundle:hipaa:1.0.0"
  ],
  "context_yaml": "..."
}


Deployment API

Deploy Bundle

POST /api/deploy
Content-Type: application/json

{
  "project_bundles": [
    {
      "id": "bundle:prior-auth",
      "version": "1.0.0"
    }
  ],
  "deployment_id": "prod-2026-01-26"
}

Response: 201 Created

{
  "deployment_id": "prod-2026-01-26",
  "project_bundles": ["bundle:prior-auth:1.0.0"],
  "resolved_bundles": [
    "bundle:meta:1.0.0",
    "bundle:hipaa:1.0.0",
    "bundle:acme-health-corp:1.0.0",
    "bundle:prior-auth:1.0.0"
  ],
  "status": "active",
  "created_at": "2026-01-26T14:00:00Z"
}


Get Deployment

GET /api/deployments/{deployment_id}

Response: 200 OK


List Deployments

GET /api/deployments?status=active

Response: 200 OK


Audit API

Verify Audit Chain Integrity

Walks the entire hash-chained audit log and recomputes every record hash.

GET /api/audit/verify
X-API-Key: sk_your_api_key_here

Response: 200 OK

{
  "status": "ok",
  "record_count": 1042,
  "legacy_record_count": 0,
  "first_chained_sequence": 1,
  "last_verified_at": "2026-04-19T10:30:00Z",
  "tampered_at_sequence": null,
  "error": null
}

If tampering is detected:

{
  "status": "tampered",
  "record_count": 1042,
  "legacy_record_count": 0,
  "first_chained_sequence": 1,
  "last_verified_at": "2026-04-19T10:30:00Z",
  "tampered_at_sequence": 214,
  "error": null
}

Authentication: Admin API key required (X-API-Key header).


Telemetry API

Telemetry endpoints are served on port 8004 (not 8000).

Get Telemetry Config

GET /config

Response: 200 OK

{
  "enabled": false,
  "endpoint": null,
  "deployment_id": "uuid-here",
  "daily_report_hour": 0,
  "retry_max_attempts": 5,
  "retry_backoff_base": 60
}


Get Telemetry Stats

GET /stats

Response: 200 OK

{
  "total_reports_sent": 12,
  "total_reports_failed": 0,
  "last_report_at": "2026-04-19T00:00:00Z",
  "queue_depth": 0
}


Get Telemetry Log

GET /log?limit=50&offset=0

Query Parameters: - limit — max entries to return (default: 50) - offset — pagination offset

Response: 200 OK

[
  {
    "id": 1,
    "reported_at": "2026-04-19T00:00:00Z",
    "status": "sent",
    "agent_count": 5,
    "context_request_count": 120,
    "bundle_count": 3
  }
]


Trigger Manual Report

POST /trigger

Forces an immediate telemetry report outside the daily schedule.

Response: 200 OK

{
  "status": "triggered"
}


Rules Engine API

Create Rule Schema

POST /api/rules/schemas
Content-Type: application/json

{
  "id": "schema:formulary",
  "schema": {
    "type": "object",
    "properties": {
      "drug_name": {"type": "string"},
      "tier": {"type": "integer"},
      "requires_pa": {"type": "boolean"}
    },
    "required": ["drug_name", "tier"]
  }
}

Response: 201 Created


Create Rule Set

POST /api/rules/rule-sets
Content-Type: application/json

{
  "id": "ruleset:formulary:acme",
  "version": "2026-Q1",
  "schema_id": "schema:formulary"
}

Response: 201 Created


Add Rule to Rule Set

POST /api/rules/rule-sets/{rule_set_id}/rules
Content-Type: application/json

{
  "rule_id": "rule:lisinopril",
  "rule_data": {
    "drug_name": "Lisinopril",
    "tier": 1,
    "requires_pa": false
  }
}

Response: 201 Created


List Rule Sets

GET /api/rules/rule-sets

Response: 200 OK


Activity Ingestion API (Collector Store)

This is a deliberately separate, standalone API — not part of the control-plane FastAPI app above, not on port 8000, and not behind X-Admin-Key. It's a minimal stdlib-only Python HTTP server (integrations/cowork-telemetry/store/app.py) reading from the SQLite database that the three activity collectors (Cowork OTel, Claude Code CLI OTel, Compliance API poller) write into. Read by the dashboard's Agent Activity tab as a second, independently-configured endpoint. See Data Dictionary for what each collector actually captures and redacts.

Base URL: http://<collector-host>:7002

Health Check

GET /health
Response: 200 OK{"status": "ok"}

List Observed Activity

GET /api/activity/observed?limit=50
limit is clamped to 1–500 (default 50). Returns the most recent rows from observed_activity_log, newest first — every column in the table (see Data Dictionary), including raw_attributes (the full redacted attribute map, for drill-down).

List Activity Sources

GET /api/activity-sources
Returns one row per collector (claude-enterprise-cowork, claude-code-cli, claude-enterprise-chat): source_id, description, event_count, first_event_at, last_event_at.

Get Source Coverage Manifest

GET /api/activity-sources/{source_id}/coverage
Returns that source's machine-readable captures / does_not_capture / caveats manifest — generated by the collector itself (COVERAGE_MANIFEST in each watcher/poller), not a hand-written doc that can drift from what's actually wired up.

Activity Stats

GET /api/activity/stats
Response: 200 OK
{
  "total_events": 412,
  "unique_users": 3,
  "unique_tools": 7,
  "fields_redacted": 89
}


Error Responses

400 Bad Request

{
  "error": "validation_error",
  "message": "Invalid bundle type",
  "details": { ... }
}

401 Unauthorized

{
  "error": "auth_failed",
  "message": "Invalid or missing API key"
}

403 Forbidden

{
  "error": "intent_not_allowed",
  "message": "Task type not in agent's allowed intents"
}

404 Not Found

{
  "error": "not_found",
  "message": "Agent not found: agent:xyz"
}

409 Conflict

{
  "error": "conflict",
  "message": "Bundle version already exists (immutable)"
}

500 Internal Server Error

{
  "error": "internal_error",
  "message": "An unexpected error occurred"
}

API Status

Endpoint Group Status Notes
Context API (v0.3) ✅ Implemented API key auth, intent validation, role-based context, optional full content
Agent Registry ✅ Implemented CRUD, API keys, revocation, lifecycle enforcement, HITL owner field
MCP Server Registry ✅ Implemented CRUD, HITL owner field — same pattern as Agent Registry
Bundle Registry ✅ Implemented CRUD, versioning, import resolution
Tag Management ✅ Implemented Create, update, resolve tags
Deployment ✅ Implemented Deploy, list, get deployments
Rules Engine ✅ Implemented Schemas, rule sets, rules
Audit API ✅ Implemented Hash-chained audit log, chain integrity verification
Telemetry API ✅ Implemented Usage stats, log, manual trigger (port 8004)
Usage/Adherence 🚧 Partial Tables exist, analytics TBD
Activity Ingestion (Collector Store) ✅ Implemented Separate standalone API, port 7002, no admin-key auth — see above

gRPC API

For streaming context, use the gRPC API on port 8002.

RequestContext (v0.3)

rpc RequestContext(ContextRequest) returns (ContextResponse);

message ContextRequest {
  string api_key = 1;
  string task_type = 2;
  map<string, string> params = 3;
}

message ContextResponse {
  bool success = 1;
  string request_id = 2;
  string agent_id = 3;
  string role_id = 4;
  string task_type = 5;
  repeated string scd_ids = 6;
  map<string, string> scd_content = 7;
  int64 timestamp = 8;
  int32 processing_time_ms = 9;
  string error_code = 10;
  string error_message = 11;
}

Subscribe (v0.2)

rpc Subscribe(SubscribeRequest) returns (stream ContextMessage);

message SubscribeRequest {
  string tag = 1;
  string agent_id = 2;
  string auth_token = 3;
}

Next Steps