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/v1Authentication
All authenticated endpoints require an API key in the Authorization header:
Authorization: Bearer sk_live_abc123def456...See Authentication for details on creating and managing API keys.
Common Headers
Request Headers
| Header | Required | Description |
|---|---|---|
Authorization | Yes* | API key (Bearer token) |
Accept | No | Should be application/json |
Content-Type | For POST/PUT | Should be application/json |
X-Workspace-ID | Sometimes | Workspace context for multi-tenant endpoints |
Idempotency-Key | No | UUID for safe retries on POST/PUT/DELETE |
*Required for authenticated endpoints
Response Headers
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests allowed in window |
X-RateLimit-Remaining | Requests remaining in current window |
X-RateLimit-Reset | Unix timestamp when limit resets |
X-Request-ID | Unique request identifier for debugging |
Common Parameters
Pagination
All list endpoints support pagination:
| Parameter | Type | Default | Max | Description |
|---|---|---|---|---|
page | integer | 1 | - | Page number |
per_page | integer | 25 | 100 | Items per page |
Response format:
{
"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:
| Parameter | Type | Description |
|---|---|---|
status | string | Filter by status (varies by resource) |
created_after | ISO 8601 date | Filter by creation date |
created_before | ISO 8601 date | Filter by creation date |
updated_after | ISO 8601 date | Filter by update date |
updated_before | ISO 8601 date | Filter by update date |
search | string | Full-text search (if supported) |
Sorting
Sort results using the sort parameter:
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:
GET /api/v1/resources?fields=id,name,created_atIncludes
Eager-load related resources:
GET /api/v1/resources?include=owner,tagsWorkspaces
List Workspaces
GET /api/v1/workspacesRequired scope: workspaces:read
Query parameters:
| Parameter | Type | Description |
|---|---|---|
page | integer | Page number |
per_page | integer | Items per page |
Response: 200 OK
{
"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
GET /api/v1/workspaces/{id}Required scope: workspaces:read
Path parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | Workspace ID |
Response: 200 OK
{
"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:
| Status | Code | Description |
|---|---|---|
| 404 | not_found | Workspace not found |
Create Workspace
POST /api/v1/workspacesRequired scope: workspaces:write
Request body:
{
"name": "New Workspace",
"slug": "new-workspace",
"tier": "pro"
}| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Workspace name (max 255 chars) |
slug | string | No | URL-friendly identifier (auto-generated if not provided) |
tier | string | No | Subscription tier (default: free) |
Response: 201 Created
{
"message": "Workspace created successfully.",
"data": {
"id": 2,
"name": "New Workspace",
"slug": "new-workspace",
"tier": "pro",
"created_at": "2026-01-15T10:30:00Z"
}
}Error responses:
| Status | Code | Description |
|---|---|---|
| 422 | validation_failed | Invalid input data |
Update Workspace
PATCH /api/v1/workspaces/{id}Required scope: workspaces:write
Request body:
{
"name": "Updated Name",
"settings": {
"timezone": "Europe/London"
}
}Response: 200 OK
Delete Workspace
DELETE /api/v1/workspaces/{id}Required scope: workspaces:delete
Response: 204 No Content
API Keys
List API Keys
GET /api/v1/api-keysRequired scope: api-keys:read
Response: 200 OK
{
"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
GET /api/v1/api-keys/{id}Required scope: api-keys:read
Response: 200 OK
{
"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
POST /api/v1/api-keysRequired scope: api-keys:write
Request body:
{
"name": "Mobile App Key",
"scopes": ["posts:read", "users:read"],
"rate_limit_tier": "pro",
"expires_at": "2027-01-01T00:00:00Z"
}| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Key name (max 255 chars) |
scopes | array | No | Permission scopes (default: read, write) |
rate_limit_tier | string | No | Rate limit tier (default: from workspace) |
expires_at | ISO 8601 | No | Expiration date (null = never) |
Response: 201 Created
{
"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
POST /api/v1/api-keys/{id}/rotateRequired scope: api-keys:write
Request body:
{
"grace_period_hours": 24
}| Field | Type | Required | Description |
|---|---|---|---|
grace_period_hours | integer | No | Hours both keys work (default: 24) |
Response: 200 OK
{
"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
DELETE /api/v1/api-keys/{id}Required scope: api-keys:delete
Response: 204 No Content
Webhooks
List Webhook Endpoints
GET /api/v1/webhooksRequired scope: webhooks:read
Response: 200 OK
{
"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
GET /api/v1/webhooks/{id}Required scope: webhooks:read
Response: 200 OK
{
"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
POST /api/v1/webhooksRequired scope: webhooks:write
Request body:
{
"url": "https://your-app.com/webhooks",
"events": ["post.created", "post.updated", "post.deleted"],
"secret": "whsec_abc123def456..."
}| Field | Type | Required | Description |
|---|---|---|---|
url | string | Yes | Webhook endpoint URL (HTTPS required) |
events | array | Yes | Events to subscribe to |
secret | string | No | Signing secret (auto-generated if not provided) |
Response: 201 Created
{
"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
PATCH /api/v1/webhooks/{id}Required scope: webhooks:write
Request body:
{
"url": "https://new-url.com/webhooks",
"events": ["post.*"],
"is_active": true
}Response: 200 OK
Delete Webhook Endpoint
DELETE /api/v1/webhooks/{id}Required scope: webhooks:delete
Response: 204 No Content
Test Webhook Endpoint
POST /api/v1/webhooks/{id}/testRequired scope: webhooks:write
Sends a test event to the webhook endpoint.
Response: 200 OK
{
"success": true,
"status_code": 200,
"response_time_ms": 145,
"response_body": "{\"received\": true}"
}Error response (delivery failed):
{
"success": false,
"status_code": 500,
"error": "Connection timeout",
"response_time_ms": 30000
}List Webhook Deliveries
GET /api/v1/webhooks/{id}/deliveriesRequired scope: webhooks:read
Query parameters:
| Parameter | Type | Description |
|---|---|---|
status | string | Filter by status: pending, success, failed, retrying |
page | integer | Page number |
per_page | integer | Items per page |
Response: 200 OK
{
"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
POST /api/v1/webhooks/{webhook_id}/deliveries/{delivery_id}/retryRequired scope: webhooks:write
Manually retry a failed delivery.
Response: 200 OK
{
"message": "Delivery queued for retry.",
"data": {
"id": 2,
"status": "pending",
"attempt": 3
}
}Error responses:
| Status | Code | Description |
|---|---|---|
| 400 | cannot_retry | Delivery already succeeded or max retries reached |
Entitlements
Check Feature Access
GET /api/v1/entitlements/checkRequired scope: entitlements:read
Query parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
feature | string | Yes | Feature key to check |
quantity | integer | No | Amount to check (default: 1) |
Response: 200 OK
{
"allowed": true,
"feature": "posts",
"current_usage": 45,
"limit": 100,
"available": 55
}Response (limit exceeded):
{
"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
POST /api/v1/entitlements/usageRequired scope: entitlements:write
Request body:
{
"feature": "api_calls",
"quantity": 1,
"metadata": {
"endpoint": "/api/v1/posts"
}
}Response: 200 OK
{
"recorded": true,
"feature": "api_calls",
"current_usage": 5001,
"limit": 10000
}Get Usage Summary
GET /api/v1/entitlements/summaryRequired scope: entitlements:read
Returns usage summary for the authenticated user's workspace.
Response: 200 OK
{
"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
POST /api/v1/seo/reportRequired scope: seo:write
Request body:
{
"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
GET /api/v1/seo/issues/{workspace_id}Required scope: seo:read
Response: 200 OK
{
"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
GET /api/v1/pixel/configAuthentication: Not required
Returns tracking pixel configuration for the current domain.
Response: 200 OK
{
"enabled": true,
"features": {
"pageviews": true,
"events": true,
"sessions": true
},
"sample_rate": 1.0
}Track Event
POST /api/v1/pixel/trackAuthentication: Not required
Rate limit: 300 requests per minute
Request body:
{
"event": "pageview",
"url": "https://example.com/page",
"referrer": "https://google.com",
"user_agent": "Mozilla/5.0...",
"properties": {
"title": "Page Title"
}
}Response: 200 OK
{
"tracked": true
}MCP (Model Context Protocol)
List MCP Servers
GET /api/v1/mcp/serversRequired scope: mcp:read
Response: 200 OK
{
"data": [
{
"id": "filesystem",
"name": "Filesystem Server",
"description": "File and directory operations",
"tools": ["read_file", "write_file", "list_directory"]
}
]
}Get MCP Server
GET /api/v1/mcp/servers/{id}Required scope: mcp:read
List Server Tools
GET /api/v1/mcp/servers/{id}/toolsRequired scope: mcp:read
Response: 200 OK
{
"data": [
{
"name": "read_file",
"description": "Read contents of a file",
"parameters": {
"path": {
"type": "string",
"description": "File path to read",
"required": true
}
}
}
]
}Call MCP Tool
POST /api/v1/mcp/tools/callRequired scope: mcp:write
Request body:
{
"server": "filesystem",
"tool": "read_file",
"arguments": {
"path": "/path/to/file.txt"
}
}Response: 200 OK
{
"result": {
"content": "File contents here...",
"size": 1234
}
}Get MCP Resource
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)
{
"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)
{
"error": "not_found",
"message": "Resource not found."
}Unauthorized (401)
{
"error": "unauthorized",
"message": "Invalid or missing API key."
}Forbidden (403)
{
"error": "access_denied",
"message": "Insufficient permissions. Required scope: posts:write"
}Feature Limit Reached (403)
{
"error": "feature_limit_reached",
"message": "You have reached your limit for this feature.",
"feature": "posts",
"upgrade_url": "https://example.com/upgrade"
}Rate Limited (429)
{
"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)
{
"error": "server_error",
"message": "An unexpected error occurred.",
"request_id": "req_abc123def456"
}Rate Limits
Rate limits vary by tier:
| Tier | Requests/Minute | Burst Allowance |
|---|---|---|
| Free | 60 | None |
| Starter | 1,000 | 20% |
| Pro | 5,000 | 30% |
| Agency | 20,000 | 50% |
| Enterprise | 100,000 | 100% |
Rate limit headers are included in every response:
X-RateLimit-Limit: 5000
X-RateLimit-Remaining: 4892
X-RateLimit-Reset: 1705312260See Rate Limiting for details.
Idempotency
For safe retries on POST, PUT, and DELETE requests, include an idempotency key:
POST /api/v1/posts
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000If 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
- Building REST APIs - Tutorial for creating API endpoints
- Authentication - API key management
- Webhooks - Event notifications
- Webhook Integration - Consumer guide
- Rate Limiting - Understanding rate limits
- Scopes - Permission system