Skip to content
Get Started

REST API & OpenAPI

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):

MethodPathDescription
GET/api/<entity-name>sList records
GET/api/<entity-name>s/:idGet single record
POST/api/<entity-name>sCreate record
PUT/api/<entity-name>s/:idUpdate record
DELETE/api/<entity-name>s/:idDelete record

Requests can authenticate via session cookies or API keys. API-key actors must have the operation-specific entity scope described below.

List endpoints support pagination:

GET /api/issues?limit=25&offset=25
ParameterDescription
limitMaximum number of results (default 100, max 1000)
offsetNumber 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.

Browser requests use session cookies automatically. Pages with auth = "required" redirect to the login page.

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.

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/json

The 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.

Authorized callers can query framework audit history for one record:

GET /api/audit?entity=Request&recordId=01J...&limit=50
Authorization: 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.

Zebric generates an OpenAPI specification from your blueprint. Access it at:

GET /api/openapi.json

The 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-capable runtimes publish:

GET /.well-known/zebric-agent.json

The 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-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/json

They 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 routes use a stable envelope:

{
"error": {
"code": "WORKFLOW_PRECONDITION_FAILED",
"message": "The workflow precondition was not satisfied",
"retryable": false,
"requestId": "req-...",
"details": {}
}
}
StatusRepresentative codesMeaning
400INVALID_REQUEST, INVALID_QUERY, INVALID_AGENT_ATTRIBUTIONInput or attribution is invalid
401AUTHENTICATION_REQUIREDBearer credential is absent or invalid
403AUTHORIZATION_FAILED, INSUFFICIENT_SCOPE, CSRF_INVALIDThe actor cannot perform the action
404NOT_FOUND, COMMAND_NOT_FOUND, RESOURCE_NOT_FOUND, WORKFLOW_NOT_FOUND, JOB_NOT_FOUNDResource or operation is absent or not visible to this actor
409IDEMPOTENCY_KEY_REUSE, WORKFLOW_PRECONDITION_FAILED, COMMAND_UNAVAILABLE, PROTECTED_FIELD_MUTATION, STATE_CONFLICTThe intended transition is stale, unavailable, or bypasses a protected field
422VALIDATION_FAILED, EXTERNAL_RESULT_VALIDATION_FAILEDCommand input or a typed external result is invalid
429RATE_LIMITEDBack off; honor Retry-After when present
500INTERNAL_ERROR, WORKFLOW_UNAVAILABLERuntime 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.