Table of Contents

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 TypePurposeAccessBilling
ManagementCRUD operations, CI/CD, seedingTools, orgs, tenants, resources, methods, rulesFree
StandardRuntime agent operationsPermission 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.

Example Request
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.io

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

StatusMeaning
200Success
201Created
204Deleted (no content)
400Bad request — missing or invalid parameters
401Unauthorized — missing or invalid API key
404Not found — resource does not exist or is not in your scope
409Conflict — resource already exists
429Rate limited — too many requests
500Internal server error

Error responses include a JSON body:

Error Response
{
  "error": "tool_name is required"
}

Data Model

oakallow's data model is a hierarchy. Every entity is scoped to a developer (you).

Hierarchy
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 → permission

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

Resolution Chain (most specific first)
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_approval

If 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).

StepKeyEndpointPurpose
1mgmtGET /v1/orgsVerify your org exists
2mgmtPOST /v1/orgs/:org/tenantsCreate tenants (your customers)
3mgmtPOST /v1/orgs/:org/resourcesCreate resources (servers, databases)
4mgmtPOST /v1/methodsRegister methods (ssh, api, cli)
5mgmtPOST /v1/categoriesCreate tool categories with defaults
6mgmtPOST /v1/tools/seedRegister your tools (org-scoped)
7mgmtPOST /v1/permissions/rulesDefine permission rules
8stdPOST /v1/permissions/checkCheck permissions at runtime
9stdPOST /v1/tokens/mintMint execution tokens
10stdPOST /v1/executions/logLog 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.

GET/v1/orgsList your organizations
Response
{
  "orgs": [
    {
      "id": "uuid",
      "external_id": "org_ftcECvT5dtdjIEgFCe6hGniT",
      "name": "My App",
      "created_at": "2026-03-21T03:49:07Z"
    }
  ],
  "count": 1
}
POST/v1/orgsCreate an organization
namestringrequired
Name for the organization.
Request
{
  "name": "My Production App"
}
Response (201)
{
  "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.

PUT/v1/orgs/:external_id/webhookSet approval webhook URL
approval_webhook_urlstring
HTTPS URL to receive approval events. Set to empty string to disable.
regenerate_secretboolean
Set to true to regenerate the signing secret.
Response
{
  "approval_webhook_url": "https://your-app.com/webhooks/oakallow",
  "webhook_secret": "a1b2c3...hex64",
  "message": "Save this secret. It will not be returned again."
}
GET/v1/orgs/:external_id/webhookGet webhook config

Tenants

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.

POST/v1/orgs/:org_external_id/tenantsCreate a tenant
namestring
Display name for the tenant.
metadataobject
Arbitrary metadata (stored as JSON).
Request
POST /v1/orgs/org_ftcECvT5dtdjIEgFCe6hGniT/tenants

{
  "name": "Acme Corp",
  "metadata": { "plan": "enterprise", "region": "us-east" }
}
Response (201)
{
  "id": "uuid",
  "external_id": "ten_lLxSMc0CwLMbgGwCPA3Y95Xe",
  "name": "Acme Corp",
  "org_id": "org_ftcECvT5dtdjIEgFCe6hGniT",
  "created_at": "2026-03-21T03:50:06Z"
}
GET/v1/orgs/:org_external_id/tenantsList tenants
Response
{
  "tenants": [
    {
      "id": "uuid",
      "external_id": "ten_lLxSMc0CwLMbgGwCPA3Y95Xe",
      "name": "Acme Corp",
      "metadata": "{}",
      "created_at": "2026-03-21T03:50:06Z"
    }
  ],
  "count": 1
}
GET/v1/orgs/:org_external_id/tenants/:tenant_external_idGet a tenant
PUT/v1/orgs/:org_external_id/tenants/:tenant_external_idUpdate a tenant
DELETE/v1/orgs/:org_external_id/tenants/:tenant_external_idDelete a tenant

Deleting 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).

POST/v1/orgs/:org_external_id/resourcesCreate a resource
external_idstringrequired
Your identifier for this resource (max 200 chars).
namestring
Display name.
metadataobject
Arbitrary metadata (IP, provider, OS, etc.).
Request
POST /v1/orgs/org_ftcECvT5dtdjIEgFCe6hGniT/resources

{
  "external_id": "server-prod-01",
  "name": "Production Server",
  "metadata": {
    "ip": "64.225.125.239",
    "provider": "digitalocean",
    "os": "linux"
  }
}
Response (201)
{
  "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..."
}
GET/v1/orgs/:org_external_id/resourcesList resources
DELETE/v1/orgs/:org_external_id/resources/:resource_external_idDelete a resource

Deleting a resource cascades to all permission rules scoped to it.

POST/v1/orgs/:org_external_id/resources/bulkBulk create resources (max 500)
Request
{
  "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.

POST/v1/methodsCreate a method
namestringrequired
Method name (unique per developer).
descriptionstring
Description of when this method is used.
Request
{
  "name": "ssh",
  "description": "Execute via SSH connection to the target server"
}
GET/v1/methodsList all methods
DELETE/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.

GET/v1/toolsList all registered tools
Response
{
  "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
}
POST/v1/toolsCreate a tool
namestringrequired
Tool name (unique per developer).
descriptionstring
What the tool does.
categorystring
Category name (e.g., "health", "security", "database").
risk_levelstring
One of: read_only, low, medium, high, critical.
required_tierstring
Minimum tier: standard.
statusstring
One of: draft, testing, approved, disabled.
default_permissionstring
Default permission if no rule matches: allowed, requires_approval, or disabled.
parametersobject
JSON schema for tool parameters.
tagsobject
Arbitrary tags for tag-based permission rules.
PUT/v1/tools/:idUpdate a tool
DELETE/v1/tools/:idDelete a tool

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

POST/v1/tools/seedBulk upsert tools (max 500)
Request
{
  "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.

Response
{
  "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.

POST/v1/permissions/checkCheck permission for a tool
tool_namestringrequired
Name of the tool to check.
tenant_idstring
Tenant external_id (if multi-tenant).
resource_idstring
Resource external_id (if resource-scoped).
methodstring
Method name (if method-scoped).
Request
{
  "tool_name": "check_memory",
  "tenant_id": "ten_lLxSMc0CwLMbgGwCPA3Y95Xe",
  "resource_id": "server-prod-01",
  "method": "ssh"
}
Response
{
  "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_fromMeaning
tenant_resource_tool_methodLevel 1: Most specific tenant rule
tenant_resource_toolLevel 2: Tenant + resource + tool
tenant_toolLevel 6: Tenant + tool (most common)
tenant_tagLevel 8: Tenant tag match
org_resource_tool_methodLevel 1: Most specific org-wide rule
org_toolLevel 6: Org-wide tool rule
tool_defaultLevel 9: Tool default_permission
category_defaultLevel 10: Category default
tool_approvedLevel 11: Tool is approved, no rules needed
fail_safeLevel 12: No rules, tool not approved → requires_approval
insufficient_tierAPI key tier too low for this tool
tool_not_foundTool 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.

POST/v1/permissions/rulesCreate or upsert a rule
org_idstringrequired
Organization external_id.
tenant_idstring
Tenant external_id (omit for org-wide rules).
resource_idstring
Resource external_id (omit for all-resource rules).
tool_namestring
Tool name (omit for wildcard tool rules).
methodstring
Method name (omit for all-method rules).
tag_keystring
Tag key for tag-based rules (Level 8).
tag_valuestring
Tag value (required if tag_key is set).
permissionstringrequired
One of: allowed, requires_approval, disabled.
Request — Tenant + Tool Rule (Level 6)
{
  "org_id": "org_ftcECvT5dtdjIEgFCe6hGniT",
  "tenant_id": "ten_lLxSMc0CwLMbgGwCPA3Y95Xe",
  "tool_name": "check_memory",
  "permission": "allowed"
}
Request — Tenant + Resource + Tool + Method Rule (Level 1)
{
  "org_id": "org_ftcECvT5dtdjIEgFCe6hGniT",
  "tenant_id": "ten_lLxSMc0CwLMbgGwCPA3Y95Xe",
  "resource_id": "server-prod-01",
  "tool_name": "restart_service",
  "method": "ssh",
  "permission": "requires_approval"
}
Request — Org-wide Wildcard (all tools require approval by default)
{
  "org_id": "org_ftcECvT5dtdjIEgFCe6hGniT",
  "permission": "requires_approval"
}
Response (201)
{
  "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.

GET/v1/permissions/rulesList rules (with optional filters)

Query parameters: org_id, tenant_id, tool_name, method.

DELETE/v1/permissions/rules/:idDelete a rule

Bulk Rules

POST/v1/permissions/rules/bulkCreate/upsert multiple rules (max 500)
Request
{
  "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"
    }
  ]
}
Response
{
  "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.

POST/v1/permissions/rules/syncReplace all rules for a scope (max 1000)
Request
{
  "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.

POST/v1/tokens/mintMint an execution token
org_idstringrequired
Organization external_id.
tool_idstringrequired
Tool UUID (from tool lookup or permission check).
paramsobject
Parameters for the execution (hashed into the token).
ttl_secondsinteger
Token lifetime in seconds (default: 300).
tenant_idstring
Tenant external_id.
Request
{
  "org_id": "org_ftcECvT5dtdjIEgFCe6hGniT",
  "tool_id": "uuid-from-permission-check",
  "params": { "threshold_percent": 90 },
  "ttl_seconds": 120,
  "tenant_id": "ten_lLxSMc0CwLMbgGwCPA3Y95Xe"
}
Response
{
  "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.

Webhook Payload — approval.created
{
  "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"
  }
}
Webhook Payload — approval.decided
{
  "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

POST/v1/approvals/requestRequest approval
org_idstringrequired
Organization external_id.
tool_namestringrequired
Tool name.
tool_idstring
Tool UUID.
paramsobject
Parameters being requested.
reasonstringrequired
Why this execution is needed.
timeout_secondsinteger
Timeout in seconds (default: 3600).
tenant_idstring
Tenant external_id.
Response
{
  "id": "uuid",
  "status": "pending",
  "expires_at": "2026-03-23T13:00:00Z"
}
GET/v1/approvals/pendingList pending approvals
GET/v1/approvals/:idCheck approval status (poll or after webhook)
POST/v1/approvals/:id/decideApprove or deny
decisionstringrequired
One of: approved or denied.
decided_bystring
Email or name of the person deciding.
notestring
Optional note from the approver.
POST/v1/approvals/:id/cancelCancel a pending approval

Execution Logging

Log every tool execution for audit trail. This is called after the tool runs, regardless of whether it succeeded or failed.

POST/v1/executions/logLog an execution
org_idstringrequired
Organization external_id.
tool_namestringrequired
Tool name.
tool_idstring
Tool UUID.
run_token_idstring
Token UUID from minting step.
execution_resultstringrequired
One of: success, failed, error, blocked.
duration_msinteger
How long the execution took.
triggered_bystringrequired
Who/what triggered this (user, agent, cron, etc.).
tenant_idstring
Tenant external_id.
metadataobject
Arbitrary metadata about the execution.
Request
{
  "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
  }
}
GET/v1/executionsQuery execution log

Query parameters: org_id, tenant_id, tool_name, execution_result.

Questions? Contact us at hello@oakallow.io