API Reference
DojOps exposes a REST API via Express with 19 endpoints covering generation, planning, diagnostics, scanning, chat, metrics, and history. Start the server with dojops serve.
Base URL
http://localhost:3000/api # Backward-compatible (no version prefix)
http://localhost:3000/api/v1 # Versioned (includes X-API-Version: 1 header)Both prefixes route to the same handlers. The /api/v1/ prefix sets the X-API-Version: 1 response header for clients that need explicit version negotiation. The port is configurable via --port flag or DOJOPS_API_PORT environment variable.
Authentication
The API supports optional API key authentication via Bearer token or X-API-Key header:
# Bearer token
curl -H "Authorization: Bearer <your-api-key>" http://localhost:3000/api/agents
# X-API-Key header
curl -H "X-API-Key: <your-api-key>" http://localhost:3000/api/agentsGenerating credentials:
dojops serve credentials # Generates API key, saves to ~/.dojops/server.jsonWhen an API key is configured (via DOJOPS_API_KEY env var or ~/.dojops/server.json), all endpoints except the minimal GET /api/health response require authentication. Without a configured API key, all endpoints are accessible without credentials.
Key comparison uses crypto.timingSafeEqual to prevent timing attacks.
Endpoints
Health
GET /api/health
Returns server status and auth requirement. When authenticated (or when auth is disabled), returns full diagnostic payload.
Response (unauthenticated, auth enabled):
{
"status": "ok",
"authRequired": true,
"timestamp": "2026-01-15T10:30:00.000Z"
}Response (authenticated or auth disabled):
{
"status": "ok",
"authRequired": false,
"provider": "openai",
"tools": ["github-actions", "terraform", "kubernetes", ...],
"metricsEnabled": true,
"timestamp": "2026-01-15T10:30:00.000Z"
}The authRequired field indicates whether the server has API key authentication configured. When authRequired is true, unauthenticated callers receive only the minimal payload (status, authRequired, timestamp) to prevent information leakage.
Generation
POST /api/generate
Agent-routed LLM generation. DojOps routes the prompt to the most relevant specialist agent.
Request:
{
"prompt": "Create a Kubernetes deployment for nginx",
"temperature": 0.7
}| Field | Type | Required | Description |
|---|---|---|---|
prompt | string | Yes | Natural language prompt |
temperature | number | No | LLM temperature (0-1) |
Response:
{
"content": "apiVersion: apps/v1\nkind: Deployment\n...",
"agent": {
"name": "kubernetes-specialist",
"domain": "container-orchestration",
"confidence": 0.92,
"reason": "Matched keywords: kubernetes, deployment, nginx"
},
"historyId": "gen-a1b2c3d4"
}curl:
curl -X POST http://localhost:3000/api/generate \
-H "Content-Type: application/json" \
-d '{"prompt": "Create a Kubernetes deployment for nginx", "temperature": 0.7}'Planning
POST /api/plan
Decompose a goal into a dependency-aware task graph with optional execution.
Request:
{
"goal": "Set up CI/CD for a Node.js app",
"execute": false,
"autoApprove": false
}| Field | Type | Required | Description |
|---|---|---|---|
goal | string | Yes | Goal to decompose |
execute | boolean | No | Execute after planning (default: false) |
autoApprove | boolean | No | Auto-approve execution (default: false) |
Response:
{
"graph": {
"nodes": [
{ "id": "1", "tool": "github-actions", "input": {...}, "deps": [] },
{ "id": "2", "tool": "dockerfile", "input": {...}, "deps": ["1"] }
]
},
"result": null,
"historyId": "plan-e5f6g7h8"
}curl:
curl -X POST http://localhost:3000/api/plan \
-H "Content-Type: application/json" \
-d '{"goal": "Set up CI/CD for a Node.js app", "execute": false}'Diagnostics
POST /api/debug-ci
Diagnose CI/CD log failures. Returns structured diagnosis with error type, root cause, affected files, and suggested fixes.
Request:
{
"log": "ERROR: npm ERR! ERESOLVE unable to resolve dependency tree"
}| Field | Type | Required | Description |
|---|---|---|---|
log | string | Yes | CI/CD log output |
Response:
{
"diagnosis": {
"errorType": "dependency-resolution",
"rootCause": "Conflicting peer dependency versions",
"affectedFiles": ["package.json"],
"fixes": [
{
"description": "Use --legacy-peer-deps flag",
"command": "npm install --legacy-peer-deps",
"confidence": 0.85
}
]
},
"historyId": "debug-i9j0k1l2"
}curl:
curl -X POST http://localhost:3000/api/debug-ci \
-H "Content-Type: application/json" \
-d '{"log": "ERROR: npm ERR! ERESOLVE unable to resolve dependency tree"}'POST /api/diff
Analyze infrastructure diffs for risk, cost impact, security implications, and recommendations.
Request:
{
"diff": "+ resource \"aws_s3_bucket\" \"main\" { bucket = \"my-bucket\" }",
"before": "optional previous state",
"after": "optional new state"
}| Field | Type | Required | Description |
|---|---|---|---|
diff | string | Yes | Infrastructure diff output |
before | string | No | Previous state context |
after | string | No | New state context |
Response:
{
"analysis": {
"riskLevel": "low",
"costImpact": "minimal",
"securityImpact": "none",
"rollbackComplexity": "simple",
"recommendations": ["Enable bucket versioning", "Add encryption"]
},
"historyId": "diff-m3n4o5p6"
}curl:
curl -X POST http://localhost:3000/api/diff \
-H "Content-Type: application/json" \
-d '{"diff": "+ resource \"aws_s3_bucket\" \"main\" { bucket = \"my-bucket\" }"}'Security Scanning
POST /api/scan
Run security scanners against a project directory.
Request:
{
"target": "/path/to/project",
"scanType": "all"
}| Field | Type | Required | Description |
|---|---|---|---|
target | string | No | Project path (defaults to cwd) |
scanType | string | No | all (default), security, deps, iac, sbom |
Response:
{
"id": "scan-q7r8s9t0",
"projectPath": "/path/to/project",
"timestamp": "2026-01-15T10:30:00.000Z",
"scanType": "all",
"findings": [
{
"id": "npm-1",
"tool": "npm-audit",
"severity": "HIGH",
"category": "DEPENDENCY",
"message": "prototype-pollution in lodash <4.17.21",
"recommendation": "Upgrade lodash to >=4.17.21",
"autoFixAvailable": true
}
],
"summary": {
"total": 5,
"critical": 0,
"high": 2,
"medium": 2,
"low": 1
},
"scannersRun": ["npm-audit", "trivy", "gitleaks"],
"scannersSkipped": ["pip-audit: no Python project detected"],
"durationMs": 3200,
"historyId": "scan-q7r8s9t0"
}curl:
curl -X POST http://localhost:3000/api/scan \
-H "Content-Type: application/json" \
-d '{"scanType": "security"}'Chat
POST /api/chat
Send a message to a chat session. Creates a new session if sessionId is not provided.
Request:
{
"sessionId": "chat-a1b2c3d4",
"message": "How should I set up Terraform state management?",
"agent": "terraform-specialist"
}| Field | Type | Required | Description |
|---|---|---|---|
message | string | Yes | User message |
sessionId | string | No | Existing session ID (creates new if omitted) |
agent | string | No | Pin to a specialist agent |
Response:
{
"content": "For Terraform state management, I recommend...",
"agent": "terraform-specialist",
"sessionId": "chat-a1b2c3d4"
}curl:
curl -X POST http://localhost:3000/api/chat \
-H "Content-Type: application/json" \
-d '{"message": "How should I set up Terraform state management?"}'POST /api/chat/sessions
Create a new chat session.
Request:
{
"name": "infra-planning",
"mode": "INTERACTIVE"
}| Field | Type | Required | Description |
|---|---|---|---|
name | string | No | Session name |
mode | string | No | INTERACTIVE (default) or DETERMINISTIC |
Response (201):
{
"id": "chat-a1b2c3d4",
"name": "infra-planning",
"createdAt": "2026-01-15T10:30:00.000Z",
"updatedAt": "2026-01-15T10:30:00.000Z",
"mode": "INTERACTIVE",
"messages": [],
"metadata": {
"totalTokensEstimate": 0,
"messageCount": 0
}
}curl:
curl -X POST http://localhost:3000/api/chat/sessions \
-H "Content-Type: application/json" \
-d '{"name": "infra-planning"}'GET /api/chat/sessions
List all active chat sessions, sorted by most recently updated.
Response:
[
{
"id": "chat-a1b2c3d4",
"name": "infra-planning",
"updatedAt": "2026-01-15T10:30:00.000Z",
"mode": "INTERACTIVE",
"metadata": { "messageCount": 12 }
}
]curl:
curl http://localhost:3000/api/chat/sessionsGET /api/chat/sessions/:id
Get a session’s full state by ID.
curl:
curl http://localhost:3000/api/chat/sessions/chat-a1b2c3d4DELETE /api/chat/sessions/:id
Delete a chat session.
curl:
curl -X DELETE http://localhost:3000/api/chat/sessions/chat-a1b2c3d4Agents
GET /api/agents
List all specialist agents (built-in + custom) with their domains, descriptions, keywords, and type.
Response:
[
{
"name": "terraform-specialist",
"domain": "infrastructure",
"description": "Expert in Terraform, HCL, modules, state management...",
"keywords": ["terraform", "infrastructure", "iac", "hcl", ...],
"type": "built-in"
},
{
"name": "sre-specialist",
"domain": "site-reliability",
"description": "SRE specialist for incident response and reliability",
"keywords": ["sre", "incident", "reliability", ...],
"type": "custom"
}
]curl:
curl http://localhost:3000/api/agentsHistory
GET /api/history
List execution history with optional filtering.
Query Parameters:
| Param | Type | Description |
|---|---|---|
type | string | Filter: generate, plan, debug-ci, diff, scan, chat |
limit | number | Max entries to return |
Response:
{
"entries": [...],
"count": 42
}curl:
curl "http://localhost:3000/api/history?type=generate&limit=10"GET /api/history/:id
Get a single history entry by ID.
curl:
curl http://localhost:3000/api/history/gen-a1b2c3d4DELETE /api/history
Clear all execution history.
curl:
curl -X DELETE http://localhost:3000/api/historyMetrics
Metrics endpoints are only available when the server has access to a .dojops/ project directory (auto-detected by the CLI serve command).
GET /api/metrics
Full dashboard metrics combining overview, security, and audit data.
curl:
curl http://localhost:3000/api/metricsGET /api/metrics/overview
Plan, execution, and scan aggregates.
Response:
{
"totalPlans": 15,
"totalExecutions": 12,
"successRate": 0.83,
"avgExecutionTimeMs": 4500,
"criticalFindings": 2,
"highFindings": 5,
"mostUsedCommands": {"generate": 20, "plan": 15, "scan": 8},
"recentActivity": [...]
}curl:
curl http://localhost:3000/api/metrics/overviewGET /api/metrics/security
Scan findings, severity trends, and top issues.
Response:
{
"totalScans": 8,
"severityBreakdown": {"critical": 2, "high": 5, "medium": 12, "low": 8},
"categoryBreakdown": {"SECURITY": 10, "DEPENDENCY": 8, "IAC": 5, "SECRETS": 4},
"findingsTrend": [...],
"topIssues": [...],
"scanHistory": [...]
}curl:
curl http://localhost:3000/api/metrics/securityGET /api/metrics/audit
Audit chain integrity and command distribution.
Response:
{
"totalEntries": 45,
"chainIntegrity": true,
"statusBreakdown": {"success": 38, "failure": 5, "cancelled": 2},
"commandDistribution": {"generate": 20, "apply": 12, "scan": 8, "plan": 5},
"timeline": [...]
}curl:
curl http://localhost:3000/api/metrics/auditError Responses
All endpoints return errors in a consistent format:
{
"error": "Validation failed",
"details": "prompt is required"
}Common HTTP status codes:
| Status | Meaning |
|---|---|
| 200 | Success |
| 201 | Created (new chat session) |
| 400 | Bad request (validation failed) |
| 401 | Unauthorized (missing or invalid API key) |
| 403 | Forbidden (e.g., autoApprove without auth) |
| 404 | Not found |
| 500 | Internal server error |
Request Validation
All POST endpoints validate request bodies against Zod schemas. Invalid requests return 400 with details:
{
"error": "Validation failed",
"details": [{ "path": ["prompt"], "message": "Required" }]
}DojOps Hub API
The DojOps Hub is a separate service with its own API for the tool marketplace. It uses a different authentication system from the local DojOps server.
Hub Authentication
The Hub supports two authentication methods:
| Method | Header | Use Case |
|---|---|---|
| API Token (Bearer) | Authorization: Bearer dojops_... | CLI tool publishing, programmatic access |
| Session Cookie | next-auth.session-token=... | Browser-based access (automatic via GitHub OAuth) |
All authenticated endpoints accept both methods. The Bearer token is checked first; session cookie is the fallback.
Generating an API token:
- Sign in to the Hub with GitHub OAuth
- Go to Settings → API Tokens (
/settings/tokens) - Create a named token with an expiration (1 month, 3 months, or no expiration)
- Copy the raw token — it’s shown once and stored as a SHA-256 hash
Token format: dojops_ followed by 40 hex characters. Maximum 10 tokens per user.
Token Management Endpoints
Token CRUD endpoints require session authentication only (not Bearer) — a leaked token cannot create or list other tokens.
GET /api/tokens
List the current user’s API tokens.
Response:
{
"tokens": [
{
"id": "clx1abc...",
"name": "My laptop",
"tokenPrefix": "dojops_a1b2c3",
"expiresAt": "2026-06-01T00:00:00.000Z",
"lastUsedAt": "2026-03-01T14:30:00.000Z",
"createdAt": "2026-03-01T10:00:00.000Z"
}
]
}POST /api/tokens
Create a new API token.
Request:
{
"name": "CI/CD pipeline",
"expiration": "3months"
}| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Label for the token (max 50 chars) |
expiration | string | No | 1month, 3months, or never (default: never) |
Response (201):
{
"id": "clx1abc...",
"name": "CI/CD pipeline",
"tokenPrefix": "dojops_a1b2c3",
"expiresAt": "2026-06-01T00:00:00.000Z",
"createdAt": "2026-03-01T10:00:00.000Z",
"rawToken": "dojops_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"
}The rawToken field is only included in the creation response. It cannot be retrieved again.
DELETE /api/tokens/:id
Revoke a token. Only the token owner can delete it.
Response:
{
"deleted": true
}Package Publishing Endpoint
POST /api/packages
Publish a new package or version. Requires authentication (Bearer token or session).
Request: multipart/form-data
| Field | Type | Required | Description |
|---|---|---|---|
file | File | Yes | .dops file (max 1MB) |
sha256 | string | No | Client-computed SHA-256 hash (publisher attestation) |
changelog | string | No | Version changelog |
curl example with Bearer token:
# Publish using an API token
curl -X POST https://hub.dojops.ai/api/packages \
-H "Authorization: Bearer dojops_a1b2c3d4e5f6..." \
-F "file=@my-tool.dops" \
-F "sha256=$(sha256sum my-tool.dops | cut -d' ' -f1)" \
-F "changelog=Initial release"Response (201 — new package):
{
"slug": "my-tool",
"version": "1.0.0",
"created": true
}Response (200 — new version of existing package):
{
"slug": "my-tool",
"version": "1.1.0",
"updated": true
}Hub Rate Limits
| Action | Limit | Window |
|---|---|---|
| Publish package | 5 | per hour |
| Star/unstar | 30 | per minute |
| Post comment | 10 | per minute |
| Search | 60 | per minute |
| Create token | 5 | per hour |