REST API & OpenAPI
REST Endpoints
Section titled “REST Endpoints”The runtime auto-generates REST endpoints for every entity defined in your blueprint.
Entity routes are pluralized with a trailing s (for example, Issue -> /api/issues):
| Method | Path | Description |
|---|---|---|
GET | /api/<entity-name>s | List records |
GET | /api/<entity-name>s/:id | Get single record |
POST | /api/<entity-name>s | Create record |
PUT | /api/<entity-name>s/:id | Update record |
DELETE | /api/<entity-name>s/:id | Delete record |
Requests can authenticate via session cookies or API keys. API-key actors must have the operation-specific entity scope described below.
Query Parameters
Section titled “Query Parameters”List endpoints support pagination:
GET /api/issues?limit=25&offset=25| Parameter | Description |
|---|---|
limit | Maximum number of results (default 100, max 1000) |
offset | Number of records to skip |
Pagination is applied after access-control filtering, so limit and offset count records visible to the current actor rather than inaccessible rows.
Authentication
Section titled “Authentication”Session-Based
Section titled “Session-Based”Browser requests use session cookies automatically. Pages with auth = "required" redirect to the login page.
API Keys
Section titled “API Keys”For programmatic access, send an API key in the Authorization header:
Authorization: Bearer <api-key-value>API keys are defined in the blueprint:
[[auth.apiKeys]]name = "my-agent"keyEnv = "MY_API_KEY"agentId = "content-agent"credentialId = "content-agent-production"roles = ["editor"]scopes = ["entity.post.list", "entity.post.get", "entity.post.update"]API key requests skip CSRF validation only after successful bearer authentication. They still go through transport scopes and entity access control.
Entity routes require entity.<lowercase-entity>.<operation>, using list, get, create, update, or delete. Mutating API-key requests must also send X-Agent-Run-ID; see API Keys for the complete identity and scope model.
Domain Commands
Section titled “Domain Commands”Every domain command receives a stable snake-case operation ID. For example, ApproveRequest is exposed as:
POST /api/commands/approve_request/{id}Authorization: Bearer <credential>Idempotency-Key: <stable-key-for-this-intent>X-Agent-Run-ID: <agent-invocation-id>Content-Type: application/jsonThe JSON body contains the command’s declared input. Successful commands return 200 with the command name and updated record. The runtime enforces the command’s scopes, typed input, policy, availableWhen condition, protected-field allowlist, transaction, and audit behavior.
Browser-generated command forms use POST /commands/{operationId}/{id} with session authentication and CSRF protection.
Audit History
Section titled “Audit History”Authorized callers can query framework audit history for one record:
GET /api/audit?entity=Request&recordId=01J...&limit=50Authorization: Bearer <credential>entity and recordId are required. Optional command, workflow, and actorId parameters narrow the result. limit accepts 1–200. Zebric first verifies that the caller can read the target record; it does not expose the raw audit store.
OpenAPI
Section titled “OpenAPI”Zebric generates an OpenAPI specification from your blueprint. Access it at:
GET /api/openapi.jsonThe spec includes all entities, their fields, and available operations. Use it with:
- API clients (Postman, Insomnia)
- Documentation generators (Swagger UI, Redoc)
- AI agents and code generators
- Type generation tools
Agent API discovery
Section titled “Agent API discovery”Agent-capable runtimes publish:
GET /.well-known/zebric-agent.jsonThe document includes the application name and version, absolute openapi URL, available skills, authentication methods, capability flags, and a contract object:
{ "name": "Issue Board", "version": "1.0.0", "openapi": "https://issues.example/api/openapi.json", "authentication": [{ "type": "bearer" }], "skills": ["issue_board"], "commands": [ { "name": "ApproveRequest", "operationId": "approve_request", "entity": "Request", "scopes": ["requests.approve"] } ], "capabilities": { "workflowJobs": true, "idempotency": true, "eventStream": false, "transactionalWorkflows": true, "d1BatchWorkflows": false, "domainCommands": true, "auditHistory": true }, "contract": { "version": "1", "fingerprint": "sha256:..." }}The same contract appears as x-zebric-contract in OpenAPI, whose response also has an ETag. Clients must not use a capability reported as false, and should reject mismatched fingerprints. Discovery falls back to /api/openapi.json for older runtimes that return 404 for the well-known route.
Each skill and command operation has an operationId, required scopes, and x-zebric-agent-operation metadata describing its risk, approval requirement, idempotency requirement, asynchronous behavior, and underlying workflow or command.
Workflow mutations and jobs
Section titled “Workflow mutations and jobs”Workflow-backed actions require these headers:
Authorization: Bearer <credential>Idempotency-Key: <stable-key-for-this-intent>X-Agent-Run-Id: <agent-invocation-id>Content-Type: application/jsonThey return 202 Accepted with { "success": true, "job": { "id", "workflow", "status", "url" } }. Poll the returned job.url (GET /api/jobs/{id}) using the same credential. Job visibility is ownership-isolated: a different API key cannot observe the job.
Repeating identical input with the same idempotency key returns the original successful operation. Reusing the key for a different route or input returns 409 IDEMPOTENCY_KEY_REUSE. Workflow precondition and conditional-update races also return 409 with distinct codes.
The in-process idempotency cache retains only successful 2xx responses. Failed responses are dropped so a corrected or transiently failed request can be retried. Successful entries expire after 24 hours, and the oldest entry is evicted when the cache exceeds 1,000 entries.
Agent API errors
Section titled “Agent API errors”Agent routes use a stable envelope:
{ "error": { "code": "WORKFLOW_PRECONDITION_FAILED", "message": "The workflow precondition was not satisfied", "retryable": false, "requestId": "req-...", "details": {} }}| Status | Representative codes | Meaning |
|---|---|---|
400 | INVALID_REQUEST, INVALID_QUERY, INVALID_AGENT_ATTRIBUTION | Input or attribution is invalid |
401 | AUTHENTICATION_REQUIRED | Bearer credential is absent or invalid |
403 | AUTHORIZATION_FAILED, INSUFFICIENT_SCOPE, CSRF_INVALID | The actor cannot perform the action |
404 | NOT_FOUND, COMMAND_NOT_FOUND, RESOURCE_NOT_FOUND, WORKFLOW_NOT_FOUND, JOB_NOT_FOUND | Resource or operation is absent or not visible to this actor |
409 | IDEMPOTENCY_KEY_REUSE, WORKFLOW_PRECONDITION_FAILED, COMMAND_UNAVAILABLE, PROTECTED_FIELD_MUTATION, STATE_CONFLICT | The intended transition is stale, unavailable, or bypasses a protected field |
422 | VALIDATION_FAILED, EXTERNAL_RESULT_VALIDATION_FAILED | Command input or a typed external result is invalid |
429 | RATE_LIMITED | Back off; honor Retry-After when present |
500 | INTERNAL_ERROR, WORKFLOW_UNAVAILABLE | Runtime or workflow infrastructure failed |
Clients should retry only when error.retryable is explicitly true, and should retain the same idempotency key for a mutation retry.