API Reference

The OKRunit REST API lets you create approval requests, check their status, and manage decisions programmatically. All endpoints are under the /api/v1 base path.

Prefer no-code?

You don't need to use the API directly. The easiest way to connect is to select the OKRunit node inside your automation platform (Zapier, Make, n8n, etc.) and connect your account — no code required. See Integrations →

OKRunit API Playground for testing API calls interactively
Use the API Playground in the dashboard to test API calls interactively before writing code.

Base URL

https://okrunit.com/api/v1

Authentication

OKRunit supports two authentication methods:

API Keys (recommended for server-to-server)

Each connection has an API key with the gk_ prefix followed by 64 hex characters. Pass it as a Bearer token:

Authorization: Bearer gk_a1b2c3d4e5f6...

How to get an API key

  1. 1.Go to Connections in the dashboard
  2. 2.Click New Connection
  3. 3.Give it a name and save
  4. 4.Copy the API key immediately — it's SHA-256 hashed before storage and cannot be retrieved again
Connections page showing API key management
Create and manage API keys from the Connections page.

OAuth 2.0 (with PKCE)

For user-facing integrations (like Zapier, Make, and other platforms that connect on behalf of users), OKRunit supports OAuth 2.0 with PKCE. Available scopes:

  • approvals:read — Read approval requests and their status
  • approvals:write — Create, approve, reject, and cancel approval requests
  • comments:write — Add comments to approval requests

Rate Limits

API requests are rate-limited per connection. The default limit is 100 requests per hour per connection, configurable up to 10,000/hour depending on your plan. Rate limit headers are included in every response:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 97
X-RateLimit-Reset: 1711296000

When the limit is exceeded, the API returns HTTP 429 with a Retry-After header.

Response Format

All successful responses return JSON. Error responses follow a consistent structure:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "title is required",
    "details": { ... }
  }
}

Common HTTP status codes:

CodeMeaning
200Success
201Created
400Validation error
401Missing or invalid authentication
403Insufficient permissions
404Resource not found
409Conflict (e.g. duplicate idempotency key)
429Rate limit exceeded
503Emergency stop is active

Endpoints

Approval Requests

POST/api/v1/approvals

Create a new approval request. The request is immediately visible to approvers and notifications are sent based on your routing configuration.

Request Body

{
  "title": "Delete user account #4821",
  "description": "Permanent deletion requested by user via support ticket",
  "priority": "high",
  "action_type": "user.delete",
  "callback_url": "https://your-app.com/webhooks/okrunit",
  "callback_headers": {
    "X-Custom-Token": "your-secret"
  },
  "metadata": {
    "user_id": "4821",
    "ticket_id": "SUP-1234"
  },
  "expires_at": "2026-03-25T10:00:00.000Z",
  "required_approvals": 2,
  "is_sequential": false,
  "source": "support-bot",
  "idempotency_key": "del-user-4821-20260324"
}

Response

// 201 Created
{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "org_id": "org-uuid",
  "connection_id": "conn-uuid",
  "title": "Delete user account #4821",
  "description": "Permanent deletion requested by user via support ticket",
  "status": "pending",
  "priority": "high",
  "action_type": "user.delete",
  "source": "support-bot",
  "required_approvals": 2,
  "current_approvals": 0,
  "is_sequential": false,
  "created_at": "2026-03-24T10:00:00.000Z",
  "expires_at": "2026-03-25T10:00:00.000Z"
}

Request body fields

title required — Short description of what needs approval
description required — Detailed context for the approver
priority required — One of: low, medium, high, critical
callback_url optional — URL to POST the decision to (see Webhooks)
callback_headers optional — Custom headers included in the webhook callback
action_type optional — Custom label for filtering and routing (e.g. "user.delete", "deploy.production")
metadata optional — Arbitrary JSON object passed through to the callback
expires_at optional — ISO 8601 timestamp; auto-expires if no decision by this time
required_approvals optional — Number of approvals needed (default: 1)
is_sequential optional — If true, approvers must approve in order (default: false)
source optional — Name of the integration for filtering and analytics
idempotency_key optional — Prevents duplicate requests (see Idempotency below)
GET/api/v1/approvals

List approval requests for your organization. Supports pagination, filtering by status and priority, and text search.

Response

// 200 OK
{
  "data": [
    {
      "id": "a1b2c3d4-...",
      "title": "Delete user account #4821",
      "status": "pending",
      "priority": "high",
      "created_at": "2026-03-24T10:00:00.000Z"
    }
  ],
  "pagination": {
    "page": 1,
    "page_size": 20,
    "total": 42,
    "total_pages": 3
  }
}
GET/api/v1/approvals/:id

Get full details for a single approval request, including metadata, comments count, and current approval tally.

Response

// 200 OK
{
  "id": "a1b2c3d4-...",
  "title": "Delete user account #4821",
  "description": "Permanent deletion requested by user via support ticket",
  "status": "pending",
  "priority": "high",
  "action_type": "user.delete",
  "source": "support-bot",
  "metadata": { "user_id": "4821", "ticket_id": "SUP-1234" },
  "required_approvals": 2,
  "current_approvals": 1,
  "is_sequential": false,
  "callback_url": "https://your-app.com/webhooks/okrunit",
  "created_at": "2026-03-24T10:00:00.000Z",
  "expires_at": "2026-03-25T10:00:00.000Z",
  "decided_at": null,
  "decided_by": null
}
PATCH/api/v1/approvals/:id

Approve or reject a pending approval request. The decision is delivered to the callback URL if one was provided. Requires the request to be in 'pending' status.

Request Body

{
  "decision": "approve",
  "comment": "Verified with the user. Proceeding with deletion.",
  "source": "dashboard"
}

Response

// 200 OK
{
  "id": "a1b2c3d4-...",
  "status": "approved",
  "decided_at": "2026-03-24T11:30:00.000Z",
  "decided_by": "user-uuid",
  "callback_delivered": true
}
DELETE/api/v1/approvals/:id

Cancel a pending approval request. Only requests in 'pending' status can be cancelled. The callback URL receives a cancellation notification.

Response

// 200 OK
{
  "id": "a1b2c3d4-...",
  "status": "cancelled",
  "cancelled_at": "2026-03-24T11:00:00.000Z"
}

Comments

POST/api/v1/approvals/:id/comments

Add a comment to an approval request. Comments are visible to all org members and are included in the audit trail.

Request Body

{
  "body": "Checked the database — this user has no active subscriptions. Safe to proceed."
}

Response

// 201 Created
{
  "id": "comment-uuid",
  "approval_request_id": "a1b2c3d4-...",
  "user_id": "user-uuid",
  "body": "Checked the database — this user has no active subscriptions. Safe to proceed.",
  "created_at": "2026-03-24T10:15:00.000Z"
}

Approval Steps (Multi-Step)

Multi-step approvals allow you to configure sequential or parallel approval chains. Each step can have a different approver. This is useful for workflows that require sign-off from multiple people (e.g. manager then legal).

GET/api/v1/approvals/:id/steps

Get all approval steps for a request. Steps are returned in order and include their status and assigned approver.

Response

// 200 OK
{
  "data": [
    {
      "id": "step-uuid-1",
      "approval_request_id": "a1b2c3d4-...",
      "step_order": 1,
      "approver_id": "user-uuid-1",
      "status": "approved",
      "decided_at": "2026-03-24T10:30:00.000Z"
    },
    {
      "id": "step-uuid-2",
      "approval_request_id": "a1b2c3d4-...",
      "step_order": 2,
      "approver_id": "user-uuid-2",
      "status": "pending",
      "decided_at": null
    }
  ]
}
POST/api/v1/approvals/:id/steps

Configure approval steps for a request. This replaces any existing step configuration. Each step specifies an approver and their order in the chain.

Request Body

{
  "steps": [
    { "approver_id": "user-uuid-1", "step_order": 1 },
    { "approver_id": "user-uuid-2", "step_order": 2 }
  ]
}

Response

// 201 Created
{
  "data": [
    {
      "id": "step-uuid-1",
      "step_order": 1,
      "approver_id": "user-uuid-1",
      "status": "pending"
    },
    {
      "id": "step-uuid-2",
      "step_order": 2,
      "approver_id": "user-uuid-2",
      "status": "pending"
    }
  ]
}
PATCH/api/v1/approvals/:id/steps/:stepId

Vote on a specific approval step. The step must be in 'pending' status and the authenticated user must be the assigned approver (or have admin privileges).

Request Body

{
  "decision": "approve",
  "comment": "LGTM"
}

Response

// 200 OK
{
  "id": "step-uuid-1",
  "status": "approved",
  "decided_at": "2026-03-24T10:30:00.000Z",
  "decided_by": "user-uuid-1"
}

Query Parameters

The list approvals endpoint (GET /api/v1/approvals) supports the following query parameters:

ParamTypeDefaultDescription
pageinteger1Page number
page_sizeinteger20Results per page (max 100)
statusstringFilter by status: pending, approved, rejected, cancelled, expired
prioritystringFilter by priority: low, medium, high, critical
searchstringFull-text search on title and description

Example: filtering requests

# Get all pending high-priority requests
curl "https://okrunit.com/api/v1/approvals?status=pending&priority=high" \
  -H "Authorization: Bearer gk_your_api_key"

# Search requests by keyword
curl "https://okrunit.com/api/v1/approvals?search=deploy+production" \
  -H "Authorization: Bearer gk_your_api_key"

# Paginate through results
curl "https://okrunit.com/api/v1/approvals?page=2&page_size=50" \
  -H "Authorization: Bearer gk_your_api_key"

Idempotency

To prevent duplicate approval requests (e.g. if your automation retries on a timeout), include an idempotency_key in your create request. If a request with the same key already exists within your organization, the API returns the existing request with a 200 status instead of creating a duplicate.

curl -X POST https://okrunit.com/api/v1/approvals \
  -H "Authorization: Bearer gk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Deploy v2.4.0",
    "description": "Production release",
    "priority": "high",
    "idempotency_key": "deploy-v2.4.0-20260324"
  }'

# If you send the exact same request again with the same key,
# you'll get back the existing request (200) instead of a new one (201)

Code examples

Node.js / TypeScript

const response = await fetch("https://okrunit.com/api/v1/approvals", {
  method: "POST",
  headers: {
    Authorization: "Bearer gk_your_api_key",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    title: "Deploy v2.4.0 to production",
    description: "Release includes database migration",
    priority: "high",
    callback_url: "https://your-app.com/webhooks/okrunit",
  }),
});

const approval = await response.json();
console.log(approval.id, approval.status); // "a1b2c3d4-..." "pending"

Python

import requests

response = requests.post(
    "https://okrunit.com/api/v1/approvals",
    headers={"Authorization": "Bearer gk_your_api_key"},
    json={
        "title": "Deploy v2.4.0 to production",
        "description": "Release includes database migration",
        "priority": "high",
        "callback_url": "https://your-app.com/webhooks/okrunit",
    },
)

approval = response.json()
print(approval["id"], approval["status"])  # "a1b2c3d4-..." "pending"