Table of Contents
Getting Started
Core Concepts
oakallow API Reference
Complete API reference for oakallow — security infrastructure for AI agents. Register tools, define permissions, mint execution tokens, manage approvals, and log every action.
Introduction
oakallow provides a hosted API for governing AI agent tool execution. Instead of building your own permission system, token minting, approval workflows, and audit trails, integrate with oakallow's API and get production-grade security infrastructure in minutes.
The API is organized around REST. All requests and responses use JSON. All endpoints (except health check) require authentication via API key.
Authentication
Authenticate by including your API key in the X-API-Key header on every request. API keys are scoped to a single organization. There are three key types:
| Key Type | Purpose | Access | Billing |
|---|---|---|---|
Management | CRUD operations, CI/CD, seeding | Tools, orgs, tenants, resources, methods, rules | Free |
Standard | Runtime agent operations | Permission checks, approvals. Token minting, execution logs, and other runtime calls are free. | $0.005/billable call |
Management keys are for setup and configuration — seeding tools, creating tenants, managing permission rules. They cannot access runtime endpoints (permission checks, approvals). Use these in CI/CD pipelines and configuration scripts.
Standard keys are for runtime — your agent uses these to check permissions and mint tokens. They can read (GET) management resources but cannot create or modify them.
curl https://api.oakallow.io/v1/tools \
-H "X-API-Key: oak_live_your_key_here"Keys are created in the oakallow dashboard. Each key is bound to one organization — you cannot access another organization's data with a different key. The organization scope is enforced at the edge and cannot be overridden.
Key format: oak_live_ followed by 32 hex characters. The first 12 characters are used as a lookup prefix. Never share your full API key.
Base URL
https://api.oakallow.ioAll API endpoints are prefixed with /v1. Requests are authenticated and rate-limited at the Cloudflare edge before reaching the origin server.
Errors
oakallow uses standard HTTP status codes.
| Status | Meaning |
|---|---|
200 | Success |
201 | Created |
204 | Deleted (no content) |
400 | Bad request — missing or invalid parameters |
401 | Unauthorized — missing or invalid API key |
404 | Not found — resource does not exist or is not in your scope |
409 | Conflict — resource already exists |
429 | Rate limited — too many requests |
500 | Internal server error |
Error responses include a JSON body:
{
"error": "tool_name is required"
}Data Model
oakallow's data model is a hierarchy. Every entity is scoped to a developer (you).
Developer (you — authenticated via API key)
└── Organization (your app — API key is scoped to one org)
├── Tenants (your customers)
├── Resources (servers, databases, endpoints — targets)
├── Methods (ssh, api, cli — how tools execute)
├── Tool Categories (health, security — with default permissions)
├── Tools (check_memory, restart_service, etc.)
└── Permission Rules
└── tenant + resource + tool + method → permissionOrganizations represent your application. Each API key is bound to one org.
Tenants represent your customers. They are optional — if your app is single-tenant, you can skip tenants entirely and define permissions at the org level.
Resources are the targets that tools operate on: servers, databases, API endpoints, Kubernetes namespaces, etc. You define the external_id (your own identifier).
Tools are the actions your AI agent can perform. Tools are org-scoped — each organization has its own tool catalog. The same tool name in different orgs are separate tools. Each tool has a name, category, risk level, and optional parameters schema.
Permission Rules map combinations of tenant + resource + tool + method to a permission level: allowed, requires_approval, or disabled.
Permission Resolution
When you check a permission, oakallow walks an 11-level resolution chain from most specific to least specific. The first match wins.
If tenant_id is provided, tenant-scoped rules are checked first:
Level 1: tenant + resource + tool + method
Level 2: tenant + resource + tool
Level 3: tenant + resource + method
Level 4: tenant + resource
Level 5: tenant + tool + method
Level 6: tenant + tool ← most common tenant rule
Level 7: tenant + method
Level 8: tenant + tag match
Then org-wide rules (tenant_id = NULL):
Same 8 levels as above
Defaults:
Level 9: tool.default_permission
Level 10: tool_category.default_permission
Level 11: tool_approved → allowed (if tool status is approved)
Level 12: fail_safe → requires_approvalIf a tool has status: approved and no rules or defaults match, it resolves as allowed (Level 11). Only unapproved tools fall through to the fail-saferequires_approval at Level 12.
Tier gating:Tools can require a minimum tier. If your API key's tier is lower than the tool's required_tier, the permission check returns disabled with resolved_from: "insufficient_tier".
Onboarding Flow
To fully integrate with oakallow, make API calls in this order. Steps 1-7 use a management key (free). Steps 8-10 use a standard key (billed).
| Step | Key | Endpoint | Purpose |
|---|---|---|---|
1 | mgmt | GET /v1/orgs | Verify your org exists |
2 | mgmt | POST /v1/orgs/:org/tenants | Create tenants (your customers) |
3 | mgmt | POST /v1/orgs/:org/resources | Create resources (servers, databases) |
4 | mgmt | POST /v1/methods | Register methods (ssh, api, cli) |
5 | mgmt | POST /v1/categories | Create tool categories with defaults |
6 | mgmt | POST /v1/tools/seed | Register your tools (org-scoped) |
7 | mgmt | POST /v1/permissions/rules | Define permission rules |
8 | std | POST /v1/permissions/check | Check permissions at runtime |
9 | std | POST /v1/tokens/mint | Mint execution tokens |
10 | std | POST /v1/executions/log | Log execution results |
Organizations
Organizations represent your application. Your API key is already scoped to one org. Use this endpoint to verify your org exists and retrieve its external_id.
/v1/orgsList your organizations{
"orgs": [
{
"id": "uuid",
"external_id": "org_ftcECvT5dtdjIEgFCe6hGniT",
"name": "My App",
"created_at": "2026-03-21T03:49:07Z"
}
],
"count": 1
}/v1/orgsCreate an organizationnamestringrequired{
"name": "My Production App"
}{
"id": "uuid",
"external_id": "org_ftcECvT5dtdjIEgFCe6hGniT",
"name": "My Production App",
"created_at": "2026-03-21T03:49:07Z"
}Approval Webhook Configuration
Configure via the dashboard Settings page or programmatically via the API. The webhook secret is auto-generated on first setup. To regenerate, pass regenerate_secret: true.
/v1/orgs/:external_id/webhookSet approval webhook URLapproval_webhook_urlstringregenerate_secretboolean{
"approval_webhook_url": "https://your-app.com/webhooks/oakallow",
"webhook_secret": "a1b2c3...hex64",
"message": "Save this secret. It will not be returned again."
}/v1/orgs/:external_id/webhookGet webhook configTenants
Tenants represent your customers within an organization. They are optional — if your app is single-tenant, skip this step and define permissions at the org level. Tenant external IDs are auto-generated with a ten_ prefix.
/v1/orgs/:org_external_id/tenantsCreate a tenantnamestringmetadataobjectPOST /v1/orgs/org_ftcECvT5dtdjIEgFCe6hGniT/tenants
{
"name": "Acme Corp",
"metadata": { "plan": "enterprise", "region": "us-east" }
}{
"id": "uuid",
"external_id": "ten_lLxSMc0CwLMbgGwCPA3Y95Xe",
"name": "Acme Corp",
"org_id": "org_ftcECvT5dtdjIEgFCe6hGniT",
"created_at": "2026-03-21T03:50:06Z"
}/v1/orgs/:org_external_id/tenantsList tenants{
"tenants": [
{
"id": "uuid",
"external_id": "ten_lLxSMc0CwLMbgGwCPA3Y95Xe",
"name": "Acme Corp",
"metadata": "{}",
"created_at": "2026-03-21T03:50:06Z"
}
],
"count": 1
}/v1/orgs/:org_external_id/tenants/:tenant_external_idGet a tenant/v1/orgs/:org_external_id/tenants/:tenant_external_idUpdate a tenant/v1/orgs/:org_external_id/tenants/:tenant_external_idDelete a tenantDeleting a tenant cascades to all its permission rules and resources.
Resources
Resources are targets that tools operate on — servers, databases, API endpoints, Kubernetes namespaces, S3 buckets, etc. You provide the external_id (your own identifier).
/v1/orgs/:org_external_id/resourcesCreate a resourceexternal_idstringrequirednamestringmetadataobjectPOST /v1/orgs/org_ftcECvT5dtdjIEgFCe6hGniT/resources
{
"external_id": "server-prod-01",
"name": "Production Server",
"metadata": {
"ip": "64.225.125.239",
"provider": "digitalocean",
"os": "linux"
}
}{
"id": "uuid",
"external_id": "server-prod-01",
"name": "Production Server",
"metadata": { "ip": "64.225.125.239", "provider": "digitalocean", "os": "linux" },
"created_at": "2026-03-23T..."
}/v1/orgs/:org_external_id/resourcesList resources/v1/orgs/:org_external_id/resources/:resource_external_idDelete a resourceDeleting a resource cascades to all permission rules scoped to it.
/v1/orgs/:org_external_id/resources/bulkBulk create resources (max 500){
"resources": [
{ "external_id": "server-prod-01", "name": "Prod Server", "metadata": {} },
{ "external_id": "server-staging-01", "name": "Staging Server", "metadata": {} },
{ "external_id": "db-prod", "name": "Production Database", "metadata": {} }
]
}Methods
Methods define how tools are executed — your vocabulary for execution patterns. Common methods: ssh, go_agent, api, cli,manual, scheduled.
/v1/methodsCreate a methodnamestringrequireddescriptionstring{
"name": "ssh",
"description": "Execute via SSH connection to the target server"
}/v1/methodsList all methods/v1/methods/:nameDelete a method (cascades to permission rules)Tool Categories
Categories group tools and provide a default permission for the group. If a tool has no specific permission rule and no tool-level default, the category default is used.
Note: Category management endpoint is coming soon. Categories are currently created via the dashboard or when tools reference them during seeding.
Tools
Tools are the actions your AI agent can perform. Each tool has a name, description, category, risk level, parameter schema, and tier requirement.
/v1/toolsList all registered tools{
"tools": [
{
"id": "uuid",
"name": "check_memory",
"description": "Check system memory usage and availability",
"category": "health",
"risk_level": "read_only",
"required_tier": "standard",
"status": "approved",
"default_permission": null,
"parameters": {
"threshold_percent": {
"type": "integer",
"description": "Alert threshold percentage",
"default": 90
}
}
}
],
"count": 1
}/v1/toolsCreate a toolnamestringrequireddescriptionstringcategorystringrisk_levelstringread_only, low, medium, high, critical.required_tierstringstandard.statusstringdraft, testing, approved, disabled.default_permissionstringallowed, requires_approval, or disabled.parametersobjecttagsobject/v1/tools/:idUpdate a tool/v1/tools/:idDelete a toolTool Seeding
The seed endpoint is designed for bulk tool registration. It performs an idempotent upsert — creates tools that don't exist and updates those that do. You can optionally include permission rules inline with each tool.
/v1/tools/seedBulk upsert tools (max 500){
"tools": [
{
"name": "check_memory",
"description": "Check system memory usage and availability",
"category": "health",
"risk_level": "read_only",
"required_tier": "standard",
"status": "approved",
"parameters": {
"threshold_percent": {
"type": "integer",
"description": "Alert threshold percentage",
"default": 90
}
},
"tags": {
"tool_types": ["ssh"],
"os": "linux",
"readOnly": true
},
"permissions": [
{
"tenant_id": "ten_lLxSMc0CwLMbgGwCPA3Y95Xe",
"permission": "allowed"
},
{
"tenant_id": "ten_lLxSMc0CwLMbgGwCPA3Y95Xe",
"resource_id": "server-prod-01",
"permission": "allowed"
}
]
}
]
}Important: The tenant_id and resource_idreferenced in permissions must already exist. The seed endpoint looks them up by external_id — if they don't exist, the permission rule is skipped and reported in the errors array.
{
"tools_created": 1,
"tools_updated": 0,
"rules_created": 2,
"rules_updated": 0,
"errors": []
}Permission Check
Check whether a tool execution is allowed for a given context. This is the hot path — resolved at the Cloudflare edge in under 10ms.
/v1/permissions/checkCheck permission for a tooltool_namestringrequiredtenant_idstringresource_idstringmethodstring{
"tool_name": "check_memory",
"tenant_id": "ten_lLxSMc0CwLMbgGwCPA3Y95Xe",
"resource_id": "server-prod-01",
"method": "ssh"
}{
"permission": "allowed",
"resolved_from": "tenant_resource_tool_method",
"resolved_level": 1,
"tool_id": "uuid",
"tool_status": "approved",
"category": "health",
"resource_id": "server-prod-01",
"method": "ssh",
"_timing": { "resolve_ms": 4, "auth": "cache" }
}The resolved_from field tells you which level of the permission chain matched. This is invaluable for debugging why a tool is allowed or blocked.
| resolved_from | Meaning |
|---|---|
tenant_resource_tool_method | Level 1: Most specific tenant rule |
tenant_resource_tool | Level 2: Tenant + resource + tool |
tenant_tool | Level 6: Tenant + tool (most common) |
tenant_tag | Level 8: Tenant tag match |
org_resource_tool_method | Level 1: Most specific org-wide rule |
org_tool | Level 6: Org-wide tool rule |
tool_default | Level 9: Tool default_permission |
category_default | Level 10: Category default |
tool_approved | Level 11: Tool is approved, no rules needed |
fail_safe | Level 12: No rules, tool not approved → requires_approval |
insufficient_tier | API key tier too low for this tool |
tool_not_found | Tool does not exist |
Note: The org_id is not in the request body — it comes from your API key. You cannot check permissions for a different organization.
Permission Rules
Permission rules define the access control for your tools. Each rule maps a combination of tenant + resource + tool + method to a permission level.
/v1/permissions/rulesCreate or upsert a ruleorg_idstringrequiredtenant_idstringresource_idstringtool_namestringmethodstringtag_keystringtag_valuestringpermissionstringrequiredallowed, requires_approval, disabled.{
"org_id": "org_ftcECvT5dtdjIEgFCe6hGniT",
"tenant_id": "ten_lLxSMc0CwLMbgGwCPA3Y95Xe",
"tool_name": "check_memory",
"permission": "allowed"
}{
"org_id": "org_ftcECvT5dtdjIEgFCe6hGniT",
"tenant_id": "ten_lLxSMc0CwLMbgGwCPA3Y95Xe",
"resource_id": "server-prod-01",
"tool_name": "restart_service",
"method": "ssh",
"permission": "requires_approval"
}{
"org_id": "org_ftcECvT5dtdjIEgFCe6hGniT",
"permission": "requires_approval"
}{
"id": "uuid",
"org_id": "org_ftcECvT5dtdjIEgFCe6hGniT",
"tenant_id": "ten_lLxSMc0CwLMbgGwCPA3Y95Xe",
"resource_id": null,
"tool_name": "check_memory",
"method": null,
"permission": "allowed",
"created": true
}Rules are upserted — if a rule with the same combination of fields already exists, the permission is updated instead of creating a duplicate.
/v1/permissions/rulesList rules (with optional filters)Query parameters: org_id, tenant_id, tool_name, method.
/v1/permissions/rules/:idDelete a ruleBulk Rules
/v1/permissions/rules/bulkCreate/upsert multiple rules (max 500){
"rules": [
{
"org_id": "org_ftcECvT5dtdjIEgFCe6hGniT",
"tenant_id": "ten_lLxSMc0CwLMbgGwCPA3Y95Xe",
"tool_name": "check_memory",
"permission": "allowed"
},
{
"org_id": "org_ftcECvT5dtdjIEgFCe6hGniT",
"tenant_id": "ten_lLxSMc0CwLMbgGwCPA3Y95Xe",
"tool_name": "restart_service",
"resource_id": "server-prod-01",
"method": "ssh",
"permission": "requires_approval"
},
{
"org_id": "org_ftcECvT5dtdjIEgFCe6hGniT",
"tool_name": "check_memory",
"permission": "allowed"
}
]
}{
"created": 2,
"updated": 1,
"errors": []
}Sync Rules
Full state sync — replaces all existing rules for an org/tenant scope with a new set. This is what a drag-and-drop permission UI calls on Save.
/v1/permissions/rules/syncReplace all rules for a scope (max 1000){
"org_id": "org_ftcECvT5dtdjIEgFCe6hGniT",
"tenant_id": "ten_lLxSMc0CwLMbgGwCPA3Y95Xe",
"rules": [
{ "tool_name": "check_memory", "permission": "allowed" },
{ "tool_name": "check_cpu", "permission": "allowed" },
{ "tool_name": "restart_service", "permission": "requires_approval" },
{ "tool_name": "drop_caches", "permission": "disabled" }
]
}Warning: This deletes all existing rules for the given org + tenant scope before inserting the new rules. Omit tenant_id to sync org-level rules.
Token Minting
Execution tokens are single-use, time-limited HMAC tokens that authorize a specific tool execution. Mint a token after the permission check passes, then include it when executing the tool.
/v1/tokens/mintMint an execution tokenorg_idstringrequiredtool_idstringrequiredparamsobjectttl_secondsintegertenant_idstring{
"org_id": "org_ftcECvT5dtdjIEgFCe6hGniT",
"tool_id": "uuid-from-permission-check",
"params": { "threshold_percent": 90 },
"ttl_seconds": 120,
"tenant_id": "ten_lLxSMc0CwLMbgGwCPA3Y95Xe"
}{
"token_id": "uuid",
"tool_id": "uuid",
"params_hash": "sha256-hex",
"nonce": "unique-nonce",
"expires_at": "2026-03-23T12:02:00Z",
"hmac": "hex-signature"
}Approvals
When a permission check returns requires_approval, your agent should request approval before executing the tool. Approvals have a configurable timeout (default: 1 hour).
Approval Webhooks
Configure an approval webhook URL per organization in the Settings page. When an approval request is created, oakallow sends a POST to your webhook URL with the event type approval.created and the full approval details (tool name, parameters, reason, approval ID, expiration). When a human decides, another webhook fires with approval.decidedand the decision.
Payloads are signed with HMAC-SHA256 using your webhook secret. Verify the X-Oakallow-Signature header (sha256=<hex>) to confirm authenticity. You can also poll GET /v1/approvals/:id as an alternative or fallback.
{
"event": "approval.created",
"timestamp": "2026-03-24T12:00:00Z",
"org_id": "org_ftcECvT5dtdjIEgFCe6hGniT",
"data": {
"approval_id": "uuid",
"tool_name": "restart_service",
"params": { "service": "nginx" },
"reason": "High CPU detected on web server",
"status": "pending",
"expires_at": "2026-03-24T13:00:00Z",
"tenant_id": "ten_lLxSMc0CwLMbgGwCPA3Y95Xe"
}
}{
"event": "approval.decided",
"timestamp": "2026-03-24T12:05:00Z",
"org_id": "org_ftcECvT5dtdjIEgFCe6hGniT",
"data": {
"approval_id": "uuid",
"tool_name": "restart_service",
"decision": "approved",
"decided_by": "admin@example.com",
"note": "Confirmed safe to restart"
}
}API Endpoints
/v1/approvals/requestRequest approvalorg_idstringrequiredtool_namestringrequiredtool_idstringparamsobjectreasonstringrequiredtimeout_secondsintegertenant_idstring{
"id": "uuid",
"status": "pending",
"expires_at": "2026-03-23T13:00:00Z"
}/v1/approvals/pendingList pending approvals/v1/approvals/:idCheck approval status (poll or after webhook)/v1/approvals/:id/decideApprove or denydecisionstringrequiredapproved or denied.decided_bystringnotestring/v1/approvals/:id/cancelCancel a pending approvalExecution Logging
Log every tool execution for audit trail. This is called after the tool runs, regardless of whether it succeeded or failed.
/v1/executions/logLog an executionorg_idstringrequiredtool_namestringrequiredtool_idstringrun_token_idstringexecution_resultstringrequiredsuccess, failed, error, blocked.duration_msintegertriggered_bystringrequiredtenant_idstringmetadataobject{
"org_id": "org_ftcECvT5dtdjIEgFCe6hGniT",
"tool_name": "check_memory",
"tool_id": "uuid",
"run_token_id": "uuid",
"execution_result": "success",
"duration_ms": 42,
"triggered_by": "ai_agent",
"tenant_id": "ten_lLxSMc0CwLMbgGwCPA3Y95Xe",
"metadata": {
"memory_used_percent": 67,
"memory_total_gb": 8
}
}/v1/executionsQuery execution logQuery parameters: org_id, tenant_id, tool_name, execution_result.
Questions? Contact us at hello@oakallow.io