API Reference¶
Zentinelle exposes two API surfaces:
- REST API (
/api/zentinelle/v1/) — agent-facing. Used by SDKs and direct agent integrations. - GraphQL (
/gql/zentinelle/) — management portal. Used by the GRC frontend. Full CRUD.
REST API (Agent-Facing)¶
Base URL: https://your-zentinelle.example.com/api/zentinelle/v1
Authentication¶
POST /registerrequiresX-Zentinelle-Bootstrap: bt_<tenant_id>_<signature>- All other agent endpoints require
X-Zentinelle-Key: sk_agent_... - In standalone mode, bootstrap tokens are HMAC-signed with
ZENTINELLE_BOOTSTRAP_SECRET
Core SDK Endpoints¶
| Method | Path | Purpose |
|---|---|---|
POST |
/register |
Register an agent and mint its runtime API key |
GET |
/config/{agent_id} |
Fetch current config and effective policies |
GET |
/secrets |
Fetch secrets for the authenticated agent |
GET |
/secrets/{agent_id} |
SDK-compatible secrets lookup scoped to one agent |
POST |
/evaluate |
Evaluate a guarded action before execution |
POST |
/events |
Submit buffered telemetry, audit, and alert events |
POST |
/heartbeat |
Report health and liveness |
POST |
/deregister |
Deactivate an agent endpoint |
Additional compliance and reporting endpoints also live under /api/zentinelle/v1, but the sections below focus on the runtime contract consumed by the SDKs.
POST /register¶
Register a new agent and return the runtime key for all subsequent calls.
Auth: X-Zentinelle-Bootstrap: bt_<tenant_id>_<signature>
Request:
{
"agent_id": "codex-dev-agent",
"name": "Codex Dev Agent",
"agent_type": "codex",
"capabilities": ["chat", "tool:search"],
"metadata": {
"version": "1.2.0",
"framework": "codex"
}
}
Response 201:
{
"agent_id": "codex-dev-agent",
"api_key": "sk_agent_...",
"config": {
"heartbeat_interval_seconds": 60,
"event_batch_size": 100,
"event_flush_interval_seconds": 5,
"config_refresh_interval_seconds": 300
},
"policies": []
}
Notes:
- agent_id is optional. If omitted, the server generates a slug from agent_type.
- name is optional. If omitted, it defaults to the final agent_id.
- agent_type must be one of the backend enum values such as codex, claude_code, gemini, langchain, or custom.
- api_key is only returned during registration. SDKs must switch to X-Zentinelle-Key after this response.
GET /config/{agent_id}¶
Fetch runtime config and effective policies for the authenticated agent.
Auth: X-Zentinelle-Key: sk_agent_...
Response 200:
{
"agent_id": "codex-dev-agent",
"config": {
"heartbeat_interval_seconds": 60,
"event_batch_size": 100,
"event_flush_interval_seconds": 5,
"config_refresh_interval_seconds": 300
},
"policies": [
{
"id": "8f08664c-8f3e-4df7-b117-4af39b4d1fe0",
"name": "Tool Allowlist",
"type": "tool_permission",
"enforcement": "enforce",
"config": {
"allowed_tools": ["web_search"]
}
}
],
"updated_at": "2026-04-09T18:00:00+00:00"
}
Notes:
- The {agent_id} path must match the authenticated agent or the server returns 403.
- SDKs cache this response and refresh it periodically or after a heartbeat drift signal.
GET /secrets¶
Return secrets scoped to the authenticated agent.
Auth: X-Zentinelle-Key: sk_agent_...
Response 200:
GET /secrets/{agent_id}¶
SDK-compatible variant of the secrets endpoint. The authenticated agent may only request its own agent_id.
Notes:
- In this standalone repo, managed secret bundles are stubbed, so the endpoint currently returns an empty payload when no secrets are provisioned.
- SDKs typically call /secrets/{agent_id}; /secrets is a convenience alias for the current agent.
POST /evaluate¶
Check an action against active policies before executing it.
Auth: X-Zentinelle-Key: sk_agent_...
Request:
{
"agent_id": "codex-dev-agent",
"action": "tool_call",
"user_id": "user_xyz",
"context": {
"tool": "web_search"
}
}
Response 200 (allowed):
{
"allowed": true,
"reason": null,
"policies_evaluated": [
{
"id": "8f08664c-8f3e-4df7-b117-4af39b4d1fe0",
"name": "Tool Allowlist",
"type": "tool_permission",
"result": "pass",
"message": null
}
],
"warnings": [],
"context": {
"tool": "web_search"
}
}
Response 200 (blocked):
{
"allowed": false,
"reason": "Tool web_search is not permitted",
"policies_evaluated": [
{
"id": "8f08664c-8f3e-4df7-b117-4af39b4d1fe0",
"name": "Tool Allowlist",
"type": "tool_permission",
"result": "fail",
"message": "Tool web_search is not permitted"
}
],
"warnings": [],
"context": {
"tool": "web_search"
}
}
Notes:
- Always returns 200. Check allowed field. Non-2xx means service error.
- policies_evaluated entries use result: "pass" | "fail" plus an optional message.
- The request agent_id must match the authenticated agent or the server returns 403.
POST /events¶
Ingest telemetry, audit, or alert events in bulk.
Auth: X-Zentinelle-Key: sk_agent_...
Request:
{
"agent_id": "codex-dev-agent",
"events": [
{
"type": "tool_call",
"category": "audit",
"timestamp": "2026-03-14T18:00:00Z",
"user_id": "user_xyz",
"payload": {
"tool": "web_search",
"duration_ms": 1420
}
}
]
}
Response 202:
Notes:
- category must be one of telemetry, audit, or alert.
- type is free-form and stored as the event type.
- 202 Accepted means queued for processing, not yet committed to DB.
POST /heartbeat¶
Report agent health and liveness.
Auth: X-Zentinelle-Key: sk_agent_...
Request:
{
"agent_id": "codex-dev-agent",
"status": "healthy",
"metrics": {
"requests_processed": 142,
"errors_last_5min": 0,
"avg_latency_ms": 1340
},
"config_hash": "sha256:abc123",
"secrets_hash": "sha256:def456",
"version": "1.2.0",
"telemetry": {
"uptime_seconds": 3600
}
}
Response 202:
Notes:
- status must be one of healthy, degraded, unhealthy, or unknown.
- The current standalone implementation returns {"acknowledged": true} and HTTP 202.
- SDKs should tolerate future optional fields such as drift or sync signals in the heartbeat response.
GraphQL API (Management Portal)¶
Endpoint: POST /gql/zentinelle/
Auth: Authorization: Bearer <user_jwt>
The management GraphQL API provides full CRUD for all Zentinelle resources. Used exclusively by the GRC portal frontend.
Key Query Types¶
type AgentEndpointType {
id: UUID!
agentId: String! # slug identifier
apiKeyPrefix: String!
tenantId: String!
status: AgentStatus!
health: HealthStatus!
capabilities: [String!]!
lastHeartbeat: DateTime
createdAt: DateTime!
}
type PolicyType {
id: UUID!
name: String!
policyType: PolicyTypeEnum!
scopeType: ScopeType! # ORG | TEAM | DEPLOYMENT | ENDPOINT | USER
scopeId: String!
config: JSONString!
isActive: Boolean!
version: Int!
tenantId: String!
}
type EventType {
id: UUID!
agentEndpoint: AgentEndpointType!
eventType: String!
payload: JSONString!
timestamp: DateTime!
}
Schema Ordering Rule¶
All ObjectType/InputType classes MUST be defined before any Query or Mutation that references them. See CLAUDE.md.
Error Reference¶
| HTTP | Code | Description |
|---|---|---|
| 401 | unauthorized |
Missing or invalid API key |
| 403 | forbidden |
Valid key but insufficient scope |
| 429 | rate_limited |
Platform-level rate limit (not policy) |
| 503 | service_unavailable |
Zentinelle degraded — SDK fails open |
| 200 | policy_blocked |
Request rejected by policy (check allowed field) |