API reference.
The gateway exposes a REST API over HTTP and a mirrored gRPC surface. All endpoints require authentication via API key or session token and enforce per-tenant isolation.
curl -X POST http://localhost:8081/api/v1/jobs \
-H "X-API-Key: $CORDUM_API_KEY" \
-H "X-Tenant-ID: default" \
-H "Content-Type: application/json" \
-d '{
"topic": "job.hello.echo",
"input": {"message": "Hello from the API"}
}'https://api.cordum.io or your self-hosted gateway (:8081)
X-API-Key + X-Tenant-ID on every request
Tenant/IP rate limiting is enabled; exceed limits returns HTTP 429.
Authentication
Every request must include an API key and tenant ID. Session tokens (from POST /api/v1/auth/login) are accepted via the same header or as a Bearer token. RBAC enforcement is enabled with CORDUM_REQUIRE_RBAC=true (enterprise).
# API key authentication
curl -H "X-API-Key: cordum_xxxx..." \
-H "X-Tenant-ID: default" \
https://api.cordum.io/api/v1/jobs
# Session token (from /auth/login)
curl -H "Authorization: Bearer session-abc123..." \
-H "X-Tenant-ID: default" \
https://api.cordum.io/api/v1/workflowsAPI Key
Pass via X-API-Key header. OSS mode supports API key allowlists and single-tenant defaults.
RBAC Roles
admin — full access. user — standard. viewer — read-only (enterprise).
Tenant Isolation
X-Tenant-ID header on protected routes. Enterprise auth provider adds multi-tenant key/RBAC controls.
| Method | Path | Description | Auth |
|---|---|---|---|
| POST | /api/v1/auth/login | Login (user or API key) | |
| POST | /api/v1/auth/logout | Invalidate session | |
| GET | /api/v1/auth/session | Validate current session | |
| POST | /api/v1/auth/password | Change own password | |
| GET | /api/v1/auth/keys | List API keys | admin |
| POST | /api/v1/auth/keys | Create API key | admin |
| DELETE | /api/v1/auth/keys/{id} | Revoke API key | admin |
Jobs
Jobs are the atomic unit of work. Submit a job, the Safety Kernel evaluates it, and the scheduler dispatches it to a worker pool. Supports idempotency via the Idempotency-Key header.
GET /api/v1/jobs/{id} can include an output_safety object with decision, findings, and redacted/original pointers when output checks are enabled.| Method | Path | Description | Auth |
|---|---|---|---|
| POST | /api/v1/jobs | Submit a new job | |
| GET | /api/v1/jobs | List jobs (cursor pagination) | |
| GET | /api/v1/jobs/{id} | Get job details + safety decision | |
| GET | /api/v1/jobs/{id}/decisions | List safety decisions for a job | |
| POST | /api/v1/jobs/{id}/cancel | Cancel a running job | admin |
| POST | /api/v1/jobs/{id}/remediate | Re-submit with remediation | admin |
curl -X POST https://api.cordum.io/api/v1/jobs \
-H "X-API-Key: cordum_xxxx..." \
-H "X-Tenant-ID: default" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"topic": "job.inference",
"capability": "gpt-4",
"risk_tags": ["high-cost"],
"context": {
"prompt": "Summarize Q4 earnings report"
}
}'
# Response:
# { "job_id": "uuid", "trace_id": "uuid" }curl -X POST https://api.cordum.io/api/v1/jobs/{id}/cancel \
-H "X-API-Key: cordum_xxxx..." \
-H "X-Tenant-ID: default"
# Response:
# { "id": "job-uuid", "state": "CANCELLED" }Workflows
Workflows define DAGs of steps. Create a definition, then start runs against it. The dry-run endpoint previews policy decisions for every step without executing.
| Method | Path | Description | Auth |
|---|---|---|---|
| POST | /api/v1/workflows | Create or update a workflow | admin |
| GET | /api/v1/workflows | List workflows | |
| GET | /api/v1/workflows/{id} | Get workflow definition | |
| DELETE | /api/v1/workflows/{id} | Delete a workflow | admin |
| POST | /api/v1/workflows/{id}/runs | Start a workflow run | admin |
| POST | /api/v1/workflows/{id}/dry-run | Dry-run policy preview | admin |
curl -X POST https://api.cordum.io/api/v1/workflows/deploy.pipeline/runs \
-H "X-API-Key: cordum_xxxx..." \
-H "X-Tenant-ID: default" \
-H "Content-Type: application/json" \
-d '{ "context": { "env": "production", "tag": "v2.1.0" } }'
# Response:
# { "run_id": "run-uuid" }
# Dry-run (preview policy decisions, no side effects):
curl -X POST ".../workflows/deploy.pipeline/dry-run" \
-H "X-API-Key: cordum_xxxx..." \
-H "X-Tenant-ID: default" \
-H "Content-Type: application/json" \
-d '{ "input": { "env": "production" } }'Runs
Runs are instances of a workflow execution. Query step status, inspect the append-only timeline, or rerun from a specific step.
| Method | Path | Description | Auth |
|---|---|---|---|
| GET | /api/v1/workflow-runs | List all runs (cursor pagination) | |
| GET | /api/v1/workflow-runs/{id} | Get run details with step status | |
| GET | /api/v1/workflow-runs/{id}/timeline | Get append-only timeline | |
| POST | /api/v1/workflow-runs/{id}/rerun | Rerun from a specific step | admin |
| POST | /api/v1/workflows/{id}/runs/{run_id}/cancel | Cancel run + propagate to workers | admin |
| DELETE | /api/v1/workflow-runs/{id} | Delete a run | admin |
curl https://api.cordum.io/api/v1/workflow-runs/{id}/timeline?limit=200 \
-H "X-API-Key: cordum_xxxx..." \
-H "X-Tenant-ID: default"
# Response:
# [
# { "timestamp": "...", "event": "run_started", "step_id": "" },
# { "timestamp": "...", "event": "step_dispatched", "step_id": "lint" },
# { "timestamp": "...", "event": "step_completed", "step_id": "lint", "status": "SUCCEEDED" },
# ...
# ]Approvals
When the Safety Kernel returns REQUIRE_APPROVAL, the job is held until a human approves or rejects it. Approval is bound to the policy snapshot + job hash — if either changes, the approval is invalidated.
| Method | Path | Description | Auth |
|---|---|---|---|
| GET | /api/v1/approvals | List pending approvals | |
| POST | /api/v1/approvals/{job_id}/approve | Approve a held job | admin |
| POST | /api/v1/approvals/{job_id}/reject | Reject a held job | admin |
curl -X POST https://api.cordum.io/api/v1/approvals/{job_id}/approve \
-H "X-API-Key: cordum_xxxx..." \
-H "X-Tenant-ID: default" \
-H "Content-Type: application/json" \
-d '{ "reason": "Approved by compliance team", "note": "Budget authorized" }'
# Response:
# { "job_id": "uuid", "trace_id": "uuid" }
# Job is re-published to the bus for processing.Policy
Evaluate, simulate, and explain Safety Kernel decisions. Manage policy bundles, publish snapshots, and audit changes.
| Method | Path | Description | Auth |
|---|---|---|---|
| POST | /api/v1/policy/evaluate | Evaluate policy for a request | |
| POST | /api/v1/policy/simulate | Simulate without state changes | |
| POST | /api/v1/policy/explain | Explain matched rules + reasoning | |
| GET | /api/v1/policy/snapshots | List immutable policy snapshots | |
| GET | /api/v1/policy/rules | List active policy rules | |
| GET | /api/v1/policy/bundles | List policy bundles | |
| PUT | /api/v1/policy/bundles/{id} | Update a policy bundle | admin |
| POST | /api/v1/policy/publish | Publish bundles (creates snapshot) | admin |
| POST | /api/v1/policy/rollback | Rollback to previous snapshot | admin |
| GET | /api/v1/policy/audit | List policy change audit trail |
curl -X POST https://api.cordum.io/api/v1/policy/simulate \
-H "X-API-Key: cordum_xxxx..." \
-H "X-Tenant-ID: default" \
-H "Content-Type: application/json" \
-d '{
"topic": "job.k8s.deploy",
"capability": "cluster.write",
"risk_tags": ["prod", "destructive"],
"actor_type": "service"
}'
# Response:
# {
# "decision": "REQUIRE_APPROVAL",
# "reason": "Destructive prod operation",
# "rule_id": "rule-456",
# "constraints": { "max_runtime_ms": 300000 },
# "remediations": [...]
# }Dead Letter Queue
Every non-success terminal state creates a DLQ entry. Inspect failed jobs, retry them, or delete entries.
| Method | Path | Description | Auth |
|---|---|---|---|
| GET | /api/v1/dlq | List dead-letter entries | admin |
| POST | /api/v1/dlq/{job_id}/retry | Retry a failed job from DLQ | admin |
| DELETE | /api/v1/dlq/{job_id} | Delete a DLQ entry | admin |
Config & Schemas
Configuration is scoped (system → org → team → workflow → step) and merged at evaluation time. Schemas validate workflow and step I/O.
| Method | Path | Description | Auth |
|---|---|---|---|
| GET | /api/v1/config | Get config by scope | |
| POST | /api/v1/config | Set config for a scope | admin |
| GET | /api/v1/config/effective | Get merged effective config |
| Method | Path | Description | Auth |
|---|---|---|---|
| POST | /api/v1/schemas | Register a JSON schema | admin |
| GET | /api/v1/schemas | List registered schemas | |
| GET | /api/v1/schemas/{id} | Get schema by ID | |
| DELETE | /api/v1/schemas/{id} | Delete a schema | admin |
Packs
Install, verify, and manage packs — installable overlays that add workflows, schemas, config, and policy fragments.
| Method | Path | Description | Auth |
|---|---|---|---|
| GET | /api/v1/packs | List installed packs | |
| GET | /api/v1/packs/{id} | Get pack details | |
| POST | /api/v1/packs/install | Install a pack | admin |
| POST | /api/v1/packs/{id}/verify | Verify pack integrity | admin |
| POST | /api/v1/packs/{id}/uninstall | Uninstall a pack | admin |
Artifacts, Locks & Infrastructure
Upload/download artifacts, acquire distributed resource locks, list workers, and correlate jobs via traces.
| Method | Path | Description | Auth |
|---|---|---|---|
| GET | /api/v1/artifacts/{ptr} | Download an artifact | |
| POST | /api/v1/artifacts | Upload an artifact | |
| POST | /api/v1/locks/acquire | Acquire a resource lock | admin |
| POST | /api/v1/locks/release | Release a resource lock | admin |
| GET | /api/v1/workers | List active workers | admin |
| GET | /api/v1/traces/{id} | Get correlated trace | |
| GET | /api/v1/status | Gateway status + connectivity | |
| GET | /health | Health check (no auth) |
WebSocket Stream
Connect to /api/v1/stream for real-time bus events. Authentication is passed via the WebSocket subprotocol header. Events are newline-delimited JSON in CAP v2 BusPacket format.
Connection
- URL:
ws://gateway:8081/api/v1/stream - Subprotocol:
cordum-api-key, <base64-key> - Query:
?tenant_id=default
Event Types
Events are filtered by tenant. Subscribes to sys.job.> and sys.audit.> NATS subjects.
{
"trace_id": "abc-123",
"sender_id": "scheduler",
"created_at": "2025-03-15T10:00:05Z",
"protocol_version": "2",
"payload": {
"job_result": {
"job_id": "job-uuid",
"status": "JOB_STATUS_SUCCEEDED",
"output": { "summary": "Q4 revenue up 12%" }
}
}
}MCP Endpoints
When MCP HTTP transport is enabled, the gateway exposes JSON-RPC routes for MCP clients over SSE + POST.
| Method | Path | Description | Auth |
|---|---|---|---|
| GET | /mcp/sse | Open MCP SSE stream (HTTP mode) | |
| POST | /mcp/message | Send JSON-RPC request/notification | |
| GET | /mcp/status | Get MCP runtime status |
curl -sS -X POST http://localhost:8081/mcp/message \
-H "Content-Type: application/json" \
-H "X-API-Key: $CORDUM_API_KEY" \
-H "X-Tenant-ID: default" \
-d '{"jsonrpc":"2.0","id":1,"method":"ping"}'Error Responses
All errors return a JSON envelope with an error field.
HTTP/1.1 400 Bad Request
Content-Type: application/json
{ "error": "missing required field: topic" }200Success201Resource created204Success, no body400Invalid input or missing fields401Missing or invalid credentials403Insufficient role or tenant denied404Resource not found409Conflict (idempotency / concurrent edit)413Payload too large (artifact limit)429Rate limit or backpressure500Internal server error503Store unavailable (Redis / NATS)Pagination
List endpoints use cursor-based pagination. Pass limit and cursor query parameters. Responses include a next_cursor field — when it is null, there are no more results.
# First page
GET /api/v1/jobs?limit=50
# Response
{ "items": [...], "next_cursor": 1707411599999999 }
# Next page
GET /api/v1/jobs?limit=50&cursor=1707411599999999
# Last page
{ "items": [...], "next_cursor": null }gRPC API
The gRPC surface mirrors REST on port :8080. Authenticate with the x-api-key metadata header. TLS is required in production.
import "core/protocol/proto/v1/api.proto";
service CordumApi {
rpc SubmitJob(JobRequest) returns (JobResponse);
rpc GetJobStatus(GetJobStatusRequest) returns (JobStatusResponse);
}
// Authenticate via metadata:
// x-api-key: cordum_xxxx...