Skip to content

API Endpoints Reference

Complete reference for all core-api endpoints. All endpoints follow RESTful conventions with consistent authentication, pagination, filtering, and error handling.

Base URL

https://your-domain.com/api/v1

Authentication

All authenticated endpoints require an API key in the Authorization header:

http
Authorization: Bearer sk_live_abc123def456...

See Authentication for details on creating and managing API keys.

Common Headers

Request Headers

HeaderRequiredDescription
AuthorizationYes*API key (Bearer token)
AcceptNoShould be application/json
Content-TypeFor POST/PUTShould be application/json
X-Workspace-IDSometimesWorkspace context for multi-tenant endpoints
Idempotency-KeyNoUUID for safe retries on POST/PUT/DELETE

*Required for authenticated endpoints

Response Headers

HeaderDescription
X-RateLimit-LimitMaximum requests allowed in window
X-RateLimit-RemainingRequests remaining in current window
X-RateLimit-ResetUnix timestamp when limit resets
X-Request-IDUnique request identifier for debugging

Common Parameters

Pagination

All list endpoints support pagination:

ParameterTypeDefaultMaxDescription
pageinteger1-Page number
per_pageinteger25100Items per page

Response format:

json
{
  "data": [...],
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 10,
    "per_page": 25,
    "to": 25,
    "total": 250
  },
  "links": {
    "first": "https://api.example.com/v1/resource?page=1",
    "last": "https://api.example.com/v1/resource?page=10",
    "prev": null,
    "next": "https://api.example.com/v1/resource?page=2"
  }
}

Filtering

Filter list results with query parameters:

ParameterTypeDescription
statusstringFilter by status (varies by resource)
created_afterISO 8601 dateFilter by creation date
created_beforeISO 8601 dateFilter by creation date
updated_afterISO 8601 dateFilter by update date
updated_beforeISO 8601 dateFilter by update date
searchstringFull-text search (if supported)

Sorting

Sort results using the sort parameter:

http
GET /api/v1/resources?sort=-created_at,name
  • Prefix with - for descending order
  • Default is ascending order
  • Comma-separate multiple fields

Field Selection

Request specific fields only:

http
GET /api/v1/resources?fields=id,name,created_at

Includes

Eager-load related resources:

http
GET /api/v1/resources?include=owner,tags

Workspaces

List Workspaces

http
GET /api/v1/workspaces

Required scope: workspaces:read

Query parameters:

ParameterTypeDescription
pageintegerPage number
per_pageintegerItems per page

Response: 200 OK

json
{
  "data": [
    {
      "id": 1,
      "name": "Acme Corporation",
      "slug": "acme-corp",
      "tier": "business",
      "created_at": "2026-01-01T00:00:00Z",
      "updated_at": "2026-01-15T10:30:00Z"
    }
  ],
  "meta": {...},
  "links": {...}
}

Get Workspace

http
GET /api/v1/workspaces/{id}

Required scope: workspaces:read

Path parameters:

ParameterTypeRequiredDescription
idintegerYesWorkspace ID

Response: 200 OK

json
{
  "data": {
    "id": 1,
    "name": "Acme Corporation",
    "slug": "acme-corp",
    "tier": "business",
    "settings": {
      "timezone": "UTC",
      "locale": "en_GB"
    },
    "created_at": "2026-01-01T00:00:00Z",
    "updated_at": "2026-01-15T10:30:00Z"
  }
}

Error responses:

StatusCodeDescription
404not_foundWorkspace not found

Create Workspace

http
POST /api/v1/workspaces

Required scope: workspaces:write

Request body:

json
{
  "name": "New Workspace",
  "slug": "new-workspace",
  "tier": "pro"
}
FieldTypeRequiredDescription
namestringYesWorkspace name (max 255 chars)
slugstringNoURL-friendly identifier (auto-generated if not provided)
tierstringNoSubscription tier (default: free)

Response: 201 Created

json
{
  "message": "Workspace created successfully.",
  "data": {
    "id": 2,
    "name": "New Workspace",
    "slug": "new-workspace",
    "tier": "pro",
    "created_at": "2026-01-15T10:30:00Z"
  }
}

Error responses:

StatusCodeDescription
422validation_failedInvalid input data

Update Workspace

http
PATCH /api/v1/workspaces/{id}

Required scope: workspaces:write

Request body:

json
{
  "name": "Updated Name",
  "settings": {
    "timezone": "Europe/London"
  }
}

Response: 200 OK

Delete Workspace

http
DELETE /api/v1/workspaces/{id}

Required scope: workspaces:delete

Response: 204 No Content


API Keys

List API Keys

http
GET /api/v1/api-keys

Required scope: api-keys:read

Response: 200 OK

json
{
  "data": [
    {
      "id": 1,
      "name": "Production API Key",
      "prefix": "sk_live_abc",
      "scopes": ["posts:read", "posts:write"],
      "rate_limit_tier": "pro",
      "last_used_at": "2026-01-15T10:30:00Z",
      "expires_at": null,
      "created_at": "2026-01-01T00:00:00Z"
    }
  ]
}

Note: The full API key is never returned after creation.

Get API Key

http
GET /api/v1/api-keys/{id}

Required scope: api-keys:read

Response: 200 OK

json
{
  "data": {
    "id": 1,
    "name": "Production API Key",
    "prefix": "sk_live_abc",
    "scopes": ["posts:read", "posts:write"],
    "rate_limit_tier": "pro",
    "last_used_at": "2026-01-15T10:30:00Z",
    "expires_at": null,
    "created_at": "2026-01-01T00:00:00Z"
  }
}

Create API Key

http
POST /api/v1/api-keys

Required scope: api-keys:write

Request body:

json
{
  "name": "Mobile App Key",
  "scopes": ["posts:read", "users:read"],
  "rate_limit_tier": "pro",
  "expires_at": "2027-01-01T00:00:00Z"
}
FieldTypeRequiredDescription
namestringYesKey name (max 255 chars)
scopesarrayNoPermission scopes (default: read, write)
rate_limit_tierstringNoRate limit tier (default: from workspace)
expires_atISO 8601NoExpiration date (null = never)

Response: 201 Created

json
{
  "message": "API key created successfully.",
  "data": {
    "id": 2,
    "name": "Mobile App Key",
    "key": "sk_live_abc123def456ghi789...",
    "scopes": ["posts:read", "users:read"],
    "rate_limit_tier": "pro",
    "expires_at": "2027-01-01T00:00:00Z",
    "created_at": "2026-01-15T10:30:00Z"
  }
}

Important: The key field is only returned once during creation. Store it securely.

Rotate API Key

http
POST /api/v1/api-keys/{id}/rotate

Required scope: api-keys:write

Request body:

json
{
  "grace_period_hours": 24
}
FieldTypeRequiredDescription
grace_period_hoursintegerNoHours both keys work (default: 24)

Response: 200 OK

json
{
  "message": "API key rotated successfully.",
  "data": {
    "id": 3,
    "name": "Mobile App Key",
    "key": "sk_live_new123key456...",
    "scopes": ["posts:read", "users:read"],
    "grace_period_ends_at": "2026-01-16T10:30:00Z"
  }
}

Revoke API Key

http
DELETE /api/v1/api-keys/{id}

Required scope: api-keys:delete

Response: 204 No Content


Webhooks

List Webhook Endpoints

http
GET /api/v1/webhooks

Required scope: webhooks:read

Response: 200 OK

json
{
  "data": [
    {
      "id": 1,
      "url": "https://your-app.com/webhooks",
      "events": ["post.created", "post.updated"],
      "is_active": true,
      "success_count": 150,
      "failure_count": 2,
      "last_delivery_at": "2026-01-15T10:30:00Z",
      "created_at": "2026-01-01T00:00:00Z"
    }
  ]
}

Get Webhook Endpoint

http
GET /api/v1/webhooks/{id}

Required scope: webhooks:read

Response: 200 OK

json
{
  "data": {
    "id": 1,
    "url": "https://your-app.com/webhooks",
    "events": ["post.created", "post.updated"],
    "is_active": true,
    "success_count": 150,
    "failure_count": 2,
    "consecutive_failures": 0,
    "last_delivery_at": "2026-01-15T10:30:00Z",
    "created_at": "2026-01-01T00:00:00Z"
  }
}

Create Webhook Endpoint

http
POST /api/v1/webhooks

Required scope: webhooks:write

Request body:

json
{
  "url": "https://your-app.com/webhooks",
  "events": ["post.created", "post.updated", "post.deleted"],
  "secret": "whsec_abc123def456..."
}
FieldTypeRequiredDescription
urlstringYesWebhook endpoint URL (HTTPS required)
eventsarrayYesEvents to subscribe to
secretstringNoSigning secret (auto-generated if not provided)

Response: 201 Created

json
{
  "message": "Webhook endpoint created successfully.",
  "data": {
    "id": 2,
    "url": "https://your-app.com/webhooks",
    "events": ["post.created", "post.updated", "post.deleted"],
    "secret": "whsec_abc123def456...",
    "is_active": true,
    "created_at": "2026-01-15T10:30:00Z"
  }
}

Important: The secret is only returned during creation. Store it securely.

Update Webhook Endpoint

http
PATCH /api/v1/webhooks/{id}

Required scope: webhooks:write

Request body:

json
{
  "url": "https://new-url.com/webhooks",
  "events": ["post.*"],
  "is_active": true
}

Response: 200 OK

Delete Webhook Endpoint

http
DELETE /api/v1/webhooks/{id}

Required scope: webhooks:delete

Response: 204 No Content

Test Webhook Endpoint

http
POST /api/v1/webhooks/{id}/test

Required scope: webhooks:write

Sends a test event to the webhook endpoint.

Response: 200 OK

json
{
  "success": true,
  "status_code": 200,
  "response_time_ms": 145,
  "response_body": "{\"received\": true}"
}

Error response (delivery failed):

json
{
  "success": false,
  "status_code": 500,
  "error": "Connection timeout",
  "response_time_ms": 30000
}

List Webhook Deliveries

http
GET /api/v1/webhooks/{id}/deliveries

Required scope: webhooks:read

Query parameters:

ParameterTypeDescription
statusstringFilter by status: pending, success, failed, retrying
pageintegerPage number
per_pageintegerItems per page

Response: 200 OK

json
{
  "data": [
    {
      "id": 1,
      "event_id": "evt_abc123def456",
      "event_type": "post.created",
      "status": "success",
      "response_code": 200,
      "attempt": 1,
      "delivered_at": "2026-01-15T10:30:00Z",
      "created_at": "2026-01-15T10:30:00Z"
    },
    {
      "id": 2,
      "event_id": "evt_xyz789",
      "event_type": "post.updated",
      "status": "retrying",
      "response_code": 500,
      "attempt": 2,
      "next_retry_at": "2026-01-15T10:35:00Z",
      "created_at": "2026-01-15T10:30:00Z"
    }
  ],
  "meta": {...},
  "links": {...}
}

Retry Webhook Delivery

http
POST /api/v1/webhooks/{webhook_id}/deliveries/{delivery_id}/retry

Required scope: webhooks:write

Manually retry a failed delivery.

Response: 200 OK

json
{
  "message": "Delivery queued for retry.",
  "data": {
    "id": 2,
    "status": "pending",
    "attempt": 3
  }
}

Error responses:

StatusCodeDescription
400cannot_retryDelivery already succeeded or max retries reached

Entitlements

Check Feature Access

http
GET /api/v1/entitlements/check

Required scope: entitlements:read

Query parameters:

ParameterTypeRequiredDescription
featurestringYesFeature key to check
quantityintegerNoAmount to check (default: 1)

Response: 200 OK

json
{
  "allowed": true,
  "feature": "posts",
  "current_usage": 45,
  "limit": 100,
  "available": 55
}

Response (limit exceeded):

json
{
  "allowed": false,
  "feature": "posts",
  "reason": "LIMIT_EXCEEDED",
  "message": "Post limit exceeded. Used: 100, Limit: 100",
  "current_usage": 100,
  "limit": 100,
  "available": 0,
  "upgrade_url": "https://example.com/upgrade"
}

Record Usage

http
POST /api/v1/entitlements/usage

Required scope: entitlements:write

Request body:

json
{
  "feature": "api_calls",
  "quantity": 1,
  "metadata": {
    "endpoint": "/api/v1/posts"
  }
}

Response: 200 OK

json
{
  "recorded": true,
  "feature": "api_calls",
  "current_usage": 5001,
  "limit": 10000
}

Get Usage Summary

http
GET /api/v1/entitlements/summary

Required scope: entitlements:read

Returns usage summary for the authenticated user's workspace.

Response: 200 OK

json
{
  "data": {
    "workspace_id": 1,
    "tier": "pro",
    "entitlements": {
      "posts": {
        "used": 45,
        "limit": 1000,
        "available": 955,
        "percentage": 4.5
      },
      "api_calls": {
        "used": 5001,
        "limit": 10000,
        "available": 4999,
        "percentage": 50.01,
        "reset_at": "2026-02-01T00:00:00Z"
      },
      "storage": {
        "used": 1073741824,
        "limit": 5368709120,
        "available": 4294967296,
        "percentage": 20,
        "unit": "bytes"
      }
    }
  }
}

SEO Reports

Submit SEO Report

http
POST /api/v1/seo/report

Required scope: seo:write

Request body:

json
{
  "url": "https://example.com/page",
  "scores": {
    "performance": 85,
    "accessibility": 92,
    "best_practices": 88,
    "seo": 95
  },
  "issues": [
    {
      "type": "missing_alt",
      "severity": "warning",
      "element": "img.hero-image"
    }
  ]
}

Response: 201 Created

Get SEO Issues

http
GET /api/v1/seo/issues/{workspace_id}

Required scope: seo:read

Response: 200 OK

json
{
  "data": [
    {
      "id": 1,
      "url": "https://example.com/page",
      "issue_type": "missing_alt",
      "severity": "warning",
      "details": {...},
      "created_at": "2026-01-15T10:30:00Z"
    }
  ]
}

Pixel Tracking

Get Pixel Configuration

http
GET /api/v1/pixel/config

Authentication: Not required

Returns tracking pixel configuration for the current domain.

Response: 200 OK

json
{
  "enabled": true,
  "features": {
    "pageviews": true,
    "events": true,
    "sessions": true
  },
  "sample_rate": 1.0
}

Track Event

http
POST /api/v1/pixel/track

Authentication: Not required

Rate limit: 300 requests per minute

Request body:

json
{
  "event": "pageview",
  "url": "https://example.com/page",
  "referrer": "https://google.com",
  "user_agent": "Mozilla/5.0...",
  "properties": {
    "title": "Page Title"
  }
}

Response: 200 OK

json
{
  "tracked": true
}

MCP (Model Context Protocol)

List MCP Servers

http
GET /api/v1/mcp/servers

Required scope: mcp:read

Response: 200 OK

json
{
  "data": [
    {
      "id": "filesystem",
      "name": "Filesystem Server",
      "description": "File and directory operations",
      "tools": ["read_file", "write_file", "list_directory"]
    }
  ]
}

Get MCP Server

http
GET /api/v1/mcp/servers/{id}

Required scope: mcp:read

List Server Tools

http
GET /api/v1/mcp/servers/{id}/tools

Required scope: mcp:read

Response: 200 OK

json
{
  "data": [
    {
      "name": "read_file",
      "description": "Read contents of a file",
      "parameters": {
        "path": {
          "type": "string",
          "description": "File path to read",
          "required": true
        }
      }
    }
  ]
}

Call MCP Tool

http
POST /api/v1/mcp/tools/call

Required scope: mcp:write

Request body:

json
{
  "server": "filesystem",
  "tool": "read_file",
  "arguments": {
    "path": "/path/to/file.txt"
  }
}

Response: 200 OK

json
{
  "result": {
    "content": "File contents here...",
    "size": 1234
  }
}

Get MCP Resource

http
GET /api/v1/mcp/resources/{uri}

Required scope: mcp:read

The uri can include slashes and will be URL-decoded.


Error Responses

All errors follow a consistent format:

Validation Error (422)

json
{
  "error": "validation_failed",
  "message": "The given data was invalid.",
  "errors": {
    "name": ["The name field is required."],
    "email": ["The email must be a valid email address."]
  }
}

Not Found (404)

json
{
  "error": "not_found",
  "message": "Resource not found."
}

Unauthorized (401)

json
{
  "error": "unauthorized",
  "message": "Invalid or missing API key."
}

Forbidden (403)

json
{
  "error": "access_denied",
  "message": "Insufficient permissions. Required scope: posts:write"
}

Feature Limit Reached (403)

json
{
  "error": "feature_limit_reached",
  "message": "You have reached your limit for this feature.",
  "feature": "posts",
  "upgrade_url": "https://example.com/upgrade"
}

Rate Limited (429)

json
{
  "error": "rate_limit_exceeded",
  "message": "Too many requests. Please retry after 60 seconds.",
  "retry_after": 60,
  "limit": 1000,
  "remaining": 0,
  "reset_at": "2026-01-15T11:00:00Z"
}

Server Error (500)

json
{
  "error": "server_error",
  "message": "An unexpected error occurred.",
  "request_id": "req_abc123def456"
}

Rate Limits

Rate limits vary by tier:

TierRequests/MinuteBurst Allowance
Free60None
Starter1,00020%
Pro5,00030%
Agency20,00050%
Enterprise100,000100%

Rate limit headers are included in every response:

http
X-RateLimit-Limit: 5000
X-RateLimit-Remaining: 4892
X-RateLimit-Reset: 1705312260

See Rate Limiting for details.


Idempotency

For safe retries on POST, PUT, and DELETE requests, include an idempotency key:

http
POST /api/v1/posts
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000

If the same idempotency key is used within 24 hours:

  • Same status code and response body returned
  • No duplicate resource created
  • Safe to retry failed requests

Learn More

Released under the EUPL-1.2 License.