-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Task: Write API documentation with interactive examples for Enterprise Coolify
Description
Create comprehensive, production-ready API documentation for the Coolify Enterprise Transformation project. This documentation covers all new enterprise endpoints (organizations, white-label branding, Terraform infrastructure, resource monitoring, payment processing, domain management) with interactive examples, authentication guides, and complete code samples for multiple programming languages.
Why This Task is Critical:
API documentation is the primary interface between the platform and third-party developers, integration partners, and automation scripts. Without comprehensive, accurate documentation, enterprise customers cannot effectively:
- Integrate Coolify into their existing workflows
- Automate infrastructure provisioning and deployment
- Build custom applications on top of Coolify's API
- Troubleshoot integration issues
- Understand authentication, authorization, and rate limiting
Professional API documentation transforms the platform from "possible to integrate" to "easy to integrate," directly impacting customer satisfaction, time-to-value, and platform adoption rates. Interactive examples allow developers to test API calls directly from the browser, reducing integration time from days to hours.
What Makes This Documentation Unique:
Unlike standard Coolify API docs, this enterprise documentation includes:
- Organization-Scoped Authentication: All endpoints require organization context with Sanctum tokens
- Tiered Rate Limiting: Different rate limits based on enterprise license tier (Starter, Professional, Enterprise)
- Multi-Tenant Considerations: How to prevent cross-organization data leakage
- Webhook Integrations: Payment gateway webhooks, Terraform provisioning status updates
- Complex Workflows: Multi-step processes like provisioning infrastructure → deploying applications → configuring domains
- Real-World Examples: Practical integration scenarios with error handling and retry logic
Integration Architecture:
The API documentation integrates with several existing and new systems:
- Task 61 (Enhanced API System): Documents all new API endpoints created in that task
- Task 54 (Rate Limiting): Explains tier-based rate limits and how to handle 429 responses
- Task 52 (Sanctum Extensions): Documents organization-scoped token authentication
- Task 58 (Swagger UI): Interactive API explorer embedded in documentation
- Task 57 (OpenAPI Spec): Auto-generated API reference from OpenAPI schema
Scope of Documentation:
-
Core Enterprise APIs (15+ endpoints):
- Organization CRUD operations
- Organization hierarchy management (parent/child relationships)
- User-organization role assignments
- White-label branding configuration
- Terraform infrastructure provisioning
- Resource monitoring and capacity planning
- Payment and subscription management
- Domain and DNS management
-
Authentication & Authorization (3 chapters):
- Sanctum token generation with organization context
- API key management with scoped abilities
- Role-based access control (RBAC) for API operations
-
Advanced Topics (5 chapters):
- Pagination strategies for large datasets
- WebSocket subscriptions for real-time updates
- Webhook configuration and HMAC validation
- Error handling and retry strategies
- Rate limit management and optimization
-
Interactive Examples (Swagger UI):
- Try-it-out functionality for all endpoints
- Pre-filled example requests
- Response schema validation
- Authentication token management
Output Deliverables:
- OpenAPI 3.1 Specification:
/docs/api/openapi.json(auto-generated, enhanced with examples) - Markdown Documentation:
/docs/api/*.md(human-readable guides and tutorials) - Swagger UI:
/api/documentationroute (interactive API explorer) - Code Samples:
/docs/api/examples/{language}/*.{ext}(PHP, JavaScript, Python, cURL) - Postman Collection:
/docs/api/Coolify-Enterprise.postman_collection.json - Migration Guide:
/docs/api/migration-from-standard-coolify.md
Acceptance Criteria
- OpenAPI 3.1 specification enhanced with detailed descriptions, examples, and response schemas for all 15+ enterprise endpoints
- Authentication guide with step-by-step Sanctum token generation (including organization context)
- Rate limiting documentation explaining all three tiers (Starter: 100/min, Professional: 500/min, Enterprise: 2000/min)
- Interactive Swagger UI deployed at
/api/documentationroute with working "Try it out" functionality - Code examples in 4 languages (PHP, JavaScript/Node.js, Python, cURL) for all major endpoints
- Webhook integration guide with HMAC signature validation examples
- Pagination guide with cursor-based and offset-based strategies
- Error handling reference with all HTTP status codes (400, 401, 403, 404, 422, 429, 500, 503)
- Migration guide from standard Coolify API to enterprise API
- Postman collection exported and tested with all endpoints
- Real-world workflow examples (provision infrastructure → deploy app → configure domain)
- Security best practices section (token rotation, IP allowlisting, webhook validation)
- API versioning strategy documented (current: v1, future compatibility guarantees)
- Changelog maintained for API updates
- Search functionality in documentation site
- Dark mode support in documentation UI
- Mobile-responsive documentation layout
- Copy-to-clipboard buttons for all code samples
- Documentation versioned and accessible for all API versions
- All links in documentation verified and working
Technical Details
File Paths
Documentation Files:
/home/topgun/topgun/docs/api/README.md(new - main API documentation landing page)/home/topgun/topgun/docs/api/authentication.md(new - Sanctum token guide)/home/topgun/topgun/docs/api/rate-limiting.md(new - rate limit guide)/home/topgun/topgun/docs/api/organizations.md(new - organization API reference)/home/topgun/topgun/docs/api/white-label.md(new - branding API reference)/home/topgun/topgun/docs/api/infrastructure.md(new - Terraform API reference)/home/topgun/topgun/docs/api/monitoring.md(new - resource monitoring API reference)/home/topgun/topgun/docs/api/payments.md(new - payment API reference)/home/topgun/topgun/docs/api/domains.md(new - domain management API reference)/home/topgun/topgun/docs/api/webhooks.md(new - webhook integration guide)/home/topgun/topgun/docs/api/errors.md(new - error reference)/home/topgun/topgun/docs/api/pagination.md(new - pagination strategies)/home/topgun/topgun/docs/api/migration.md(new - migration guide)/home/topgun/topgun/docs/api/changelog.md(new - API changelog)
Code Example Files:
/home/topgun/topgun/docs/api/examples/php/*.php(new - Laravel/Guzzle examples)/home/topgun/topgun/docs/api/examples/javascript/*.js(new - Node.js/Axios examples)/home/topgun/topgun/docs/api/examples/python/*.py(new - Requests library examples)/home/topgun/topgun/docs/api/examples/curl/*.sh(new - cURL examples)
OpenAPI Specification:
/home/topgun/topgun/storage/api-docs/openapi.json(enhanced - auto-generated with custom annotations)
Swagger UI Integration:
/home/topgun/topgun/resources/views/api/documentation.blade.php(new - Swagger UI view)/home/topgun/topgun/routes/web.php(modify - add/api/documentationroute)
Postman Collection:
/home/topgun/topgun/docs/api/Coolify-Enterprise.postman_collection.json(new - exported collection)
OpenAPI Specification Enhancement
File: storage/api-docs/openapi.json (enhanced with custom annotations)
The existing OpenAPI spec (generated by L5-Swagger or similar) needs enhancement with:
{
"openapi": "3.1.0",
"info": {
"title": "Coolify Enterprise API",
"description": "Comprehensive API for multi-tenant infrastructure provisioning, application deployment, and white-label branding. Supports organization hierarchies, tiered rate limiting, and real-time resource monitoring.",
"version": "1.0.0",
"contact": {
"name": "Coolify Enterprise Support",
"email": "enterprise@coolify.io",
"url": "https://enterprise.coolify.io/support"
},
"license": {
"name": "Proprietary",
"url": "https://enterprise.coolify.io/license"
}
},
"servers": [
{
"url": "https://api.coolify.io/v1",
"description": "Production API"
},
{
"url": "https://staging-api.coolify.io/v1",
"description": "Staging API"
},
{
"url": "http://localhost:8000/api/v1",
"description": "Local Development"
}
],
"security": [
{
"sanctum": ["organization:read", "organization:write"]
}
],
"components": {
"securitySchemes": {
"sanctum": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "Sanctum Token",
"description": "Use a Sanctum personal access token with organization-scoped abilities. Generate tokens via POST /api/v1/auth/tokens with organization_id and abilities array."
}
},
"schemas": {
"Organization": {
"type": "object",
"properties": {
"id": {"type": "integer", "example": 42},
"name": {"type": "string", "example": "Acme Corporation"},
"slug": {"type": "string", "example": "acme-corp"},
"type": {
"type": "string",
"enum": ["top_branch", "master_branch", "sub_user", "end_user"],
"example": "master_branch"
},
"parent_id": {"type": "integer", "nullable": true, "example": 1},
"created_at": {"type": "string", "format": "date-time"},
"updated_at": {"type": "string", "format": "date-time"}
},
"required": ["id", "name", "slug", "type"]
},
"WhiteLabelConfig": {
"type": "object",
"properties": {
"id": {"type": "integer"},
"organization_id": {"type": "integer"},
"platform_name": {"type": "string", "example": "Acme Cloud Platform"},
"primary_color": {"type": "string", "pattern": "^#[0-9A-F]{6}$", "example": "#3B82F6"},
"secondary_color": {"type": "string", "pattern": "^#[0-9A-F]{6}$", "example": "#8B5CF6"},
"accent_color": {"type": "string", "pattern": "^#[0-9A-F]{6}$", "example": "#10B981"},
"logo_url": {"type": "string", "format": "uri", "example": "https://storage.coolify.io/branding/42/logo.png"},
"favicon_url": {"type": "string", "format": "uri", "nullable": true},
"custom_css": {"type": "string", "nullable": true}
}
},
"TerraformDeployment": {
"type": "object",
"properties": {
"id": {"type": "integer"},
"organization_id": {"type": "integer"},
"provider": {"type": "string", "enum": ["aws", "digitalocean", "hetzner", "gcp", "azure"]},
"status": {"type": "string", "enum": ["pending", "planning", "applying", "completed", "failed", "destroying"]},
"instance_count": {"type": "integer", "example": 3},
"instance_type": {"type": "string", "example": "t3.medium"},
"region": {"type": "string", "example": "us-east-1"},
"terraform_output": {"type": "object", "description": "Parsed Terraform output with server IPs and IDs"}
}
},
"Error": {
"type": "object",
"properties": {
"message": {"type": "string", "example": "The given data was invalid."},
"errors": {
"type": "object",
"additionalProperties": {
"type": "array",
"items": {"type": "string"}
},
"example": {
"name": ["The name field is required."],
"email": ["The email must be a valid email address."]
}
}
}
},
"RateLimitHeaders": {
"type": "object",
"properties": {
"X-RateLimit-Limit": {"type": "integer", "description": "Maximum requests allowed per window", "example": 500},
"X-RateLimit-Remaining": {"type": "integer", "description": "Requests remaining in current window", "example": 487},
"X-RateLimit-Reset": {"type": "integer", "description": "Unix timestamp when rate limit resets", "example": 1678901234},
"Retry-After": {"type": "integer", "description": "Seconds to wait before retrying (only on 429)", "example": 42}
}
}
},
"responses": {
"Unauthorized": {
"description": "Authentication token is missing or invalid",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"message": {"type": "string", "example": "Unauthenticated."}
}
}
}
}
},
"Forbidden": {
"description": "Authenticated but lacking required permissions",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"message": {"type": "string", "example": "This action is unauthorized."}
}
}
}
}
},
"NotFound": {
"description": "Resource not found or not accessible in your organization",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"message": {"type": "string", "example": "Resource not found."}
}
}
}
}
},
"ValidationError": {
"description": "Request validation failed",
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Error"}
}
}
},
"RateLimitExceeded": {
"description": "Rate limit exceeded for your tier",
"headers": {
"X-RateLimit-Limit": {"schema": {"type": "integer"}},
"X-RateLimit-Remaining": {"schema": {"type": "integer"}},
"X-RateLimit-Reset": {"schema": {"type": "integer"}},
"Retry-After": {"schema": {"type": "integer"}}
},
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"message": {"type": "string", "example": "Too Many Requests"}
}
}
}
}
}
}
},
"paths": {
"/organizations": {
"get": {
"summary": "List all organizations accessible to the authenticated user",
"description": "Returns a paginated list of organizations where the user has any role. Includes organization hierarchy information (parent/child relationships). Results are automatically scoped to the user's access.",
"operationId": "listOrganizations",
"tags": ["Organizations"],
"security": [{"sanctum": ["organization:read"]}],
"parameters": [
{
"name": "page",
"in": "query",
"description": "Page number for pagination (1-indexed)",
"schema": {"type": "integer", "default": 1, "minimum": 1}
},
{
"name": "per_page",
"in": "query",
"description": "Number of results per page",
"schema": {"type": "integer", "default": 15, "minimum": 1, "maximum": 100}
},
{
"name": "type",
"in": "query",
"description": "Filter by organization type",
"schema": {
"type": "string",
"enum": ["top_branch", "master_branch", "sub_user", "end_user"]
}
}
],
"responses": {
"200": {
"description": "Successful response with paginated organizations",
"headers": {
"X-RateLimit-Limit": {"schema": {"type": "integer"}},
"X-RateLimit-Remaining": {"schema": {"type": "integer"}},
"X-RateLimit-Reset": {"schema": {"type": "integer"}}
},
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {"$ref": "#/components/schemas/Organization"}
},
"meta": {
"type": "object",
"properties": {
"current_page": {"type": "integer", "example": 1},
"per_page": {"type": "integer", "example": 15},
"total": {"type": "integer", "example": 47},
"last_page": {"type": "integer", "example": 4}
}
},
"links": {
"type": "object",
"properties": {
"first": {"type": "string", "format": "uri"},
"last": {"type": "string", "format": "uri"},
"prev": {"type": "string", "format": "uri", "nullable": true},
"next": {"type": "string", "format": "uri", "nullable": true}
}
}
}
},
"examples": {
"success": {
"summary": "Successful response",
"value": {
"data": [
{
"id": 42,
"name": "Acme Corporation",
"slug": "acme-corp",
"type": "master_branch",
"parent_id": 1,
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-03-20T14:22:00Z"
}
],
"meta": {
"current_page": 1,
"per_page": 15,
"total": 47,
"last_page": 4
},
"links": {
"first": "https://api.coolify.io/v1/organizations?page=1",
"last": "https://api.coolify.io/v1/organizations?page=4",
"prev": null,
"next": "https://api.coolify.io/v1/organizations?page=2"
}
}
}
}
}
}
},
"401": {"$ref": "#/components/responses/Unauthorized"},
"429": {"$ref": "#/components/responses/RateLimitExceeded"}
}
},
"post": {
"summary": "Create a new organization",
"description": "Creates a new organization under the authenticated user's current organization (if hierarchical structure). Requires 'organization:write' ability.",
"operationId": "createOrganization",
"tags": ["Organizations"],
"security": [{"sanctum": ["organization:write"]}],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"name": {"type": "string", "minLength": 1, "maxLength": 255, "example": "New Organization"},
"slug": {"type": "string", "pattern": "^[a-z0-9-]+$", "example": "new-org"},
"type": {
"type": "string",
"enum": ["top_branch", "master_branch", "sub_user", "end_user"],
"example": "master_branch"
},
"parent_id": {"type": "integer", "nullable": true, "example": 1}
},
"required": ["name", "type"]
},
"examples": {
"master_branch": {
"summary": "Create master branch organization",
"value": {
"name": "Acme Corporation",
"slug": "acme-corp",
"type": "master_branch",
"parent_id": 1
}
}
}
}
}
},
"responses": {
"201": {
"description": "Organization created successfully",
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Organization"}
}
}
},
"401": {"$ref": "#/components/responses/Unauthorized"},
"403": {"$ref": "#/components/responses/Forbidden"},
"422": {"$ref": "#/components/responses/ValidationError"},
"429": {"$ref": "#/components/responses/RateLimitExceeded"}
}
}
},
"/organizations/{id}": {
"get": {
"summary": "Get organization details",
"description": "Retrieve detailed information about a specific organization, including white-label configuration, license status, and resource usage.",
"operationId": "getOrganization",
"tags": ["Organizations"],
"security": [{"sanctum": ["organization:read"]}],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"description": "Organization ID or slug",
"schema": {"type": "string", "example": "42"}
}
],
"responses": {
"200": {
"description": "Successful response",
"content": {
"application/json": {
"schema": {
"allOf": [
{"$ref": "#/components/schemas/Organization"},
{
"type": "object",
"properties": {
"white_label_config": {"$ref": "#/components/schemas/WhiteLabelConfig"},
"license": {"type": "object"},
"resource_usage": {"type": "object"}
}
}
]
}
}
}
},
"401": {"$ref": "#/components/responses/Unauthorized"},
"403": {"$ref": "#/components/responses/Forbidden"},
"404": {"$ref": "#/components/responses/NotFound"},
"429": {"$ref": "#/components/responses/RateLimitExceeded"}
}
}
},
"/terraform/deployments": {
"post": {
"summary": "Provision cloud infrastructure",
"description": "Initiate a Terraform deployment to provision cloud servers. This is an asynchronous operation - use the returned deployment ID to poll for status.",
"operationId": "provisionInfrastructure",
"tags": ["Infrastructure"],
"security": [{"sanctum": ["infrastructure:write"]}],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"organization_id": {"type": "integer", "example": 42},
"provider": {"type": "string", "enum": ["aws", "digitalocean", "hetzner"], "example": "aws"},
"region": {"type": "string", "example": "us-east-1"},
"instance_type": {"type": "string", "example": "t3.medium"},
"instance_count": {"type": "integer", "minimum": 1, "maximum": 50, "example": 3},
"cloud_credential_id": {"type": "integer", "description": "ID of stored cloud provider credentials", "example": 7}
},
"required": ["organization_id", "provider", "region", "instance_type", "instance_count", "cloud_credential_id"]
}
}
}
},
"responses": {
"202": {
"description": "Deployment initiated successfully",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"message": {"type": "string", "example": "Infrastructure provisioning initiated"},
"deployment": {"$ref": "#/components/schemas/TerraformDeployment"}
}
}
}
}
},
"401": {"$ref": "#/components/responses/Unauthorized"},
"403": {"$ref": "#/components/responses/Forbidden"},
"422": {"$ref": "#/components/responses/ValidationError"},
"429": {"$ref": "#/components/responses/RateLimitExceeded"}
}
}
}
}
}Authentication Documentation
File: docs/api/authentication.md
# API Authentication
Coolify Enterprise uses **Laravel Sanctum** for API authentication with organization-scoped tokens.
## Generating API Tokens
### Step 1: Login to obtain session
```bash
curl -X POST https://api.coolify.io/v1/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "admin@acme.com",
"password": "your-secure-password"
}'Response:
{
"user": {
"id": 123,
"name": "Admin User",
"email": "admin@acme.com"
},
"organizations": [
{"id": 42, "name": "Acme Corporation", "role": "admin"}
]
}Step 2: Generate organization-scoped token
curl -X POST https://api.coolify.io/v1/auth/tokens \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {session-token}" \
-d '{
"name": "Production API Key",
"organization_id": 42,
"abilities": [
"organization:read",
"organization:write",
"infrastructure:read",
"infrastructure:write",
"application:deploy"
],
"expires_at": "2025-12-31"
}'Response:
{
"token": "1|aB3dEf5gHiJkLmNoPqRsTuVwXyZ",
"abilities": ["organization:read", "organization:write", ...],
"expires_at": "2025-12-31T23:59:59Z"
}Step 3: Use token in API requests
curl -X GET https://api.coolify.io/v1/organizations \
-H "Authorization: Bearer 1|aB3dEf5gHiJkLmNoPqRsTuVwXyZ" \
-H "Accept: application/json"Token Abilities (Scopes)
| Ability | Description |
|---|---|
organization:read |
View organization details, users, settings |
organization:write |
Create/update organizations, manage users |
infrastructure:read |
View servers, deployments, resource metrics |
infrastructure:write |
Provision servers, modify infrastructure |
application:read |
View applications and their configurations |
application:write |
Create/update applications |
application:deploy |
Trigger application deployments |
payment:read |
View subscriptions and billing information |
payment:write |
Update payment methods, change subscriptions |
domain:read |
View domain configurations |
domain:write |
Register domains, modify DNS records |
Security Best Practices
- Token Rotation: Rotate API tokens every 90 days
- Principle of Least Privilege: Grant only necessary abilities
- IP Allowlisting: Restrict tokens to known IP ranges (enterprise tier)
- Token Expiration: Always set expiration dates
- Secure Storage: Store tokens in environment variables or secrets managers (never commit to git)
Example: Multi-Language Integration
PHP (Laravel/Guzzle)
use GuzzleHttp\Client;
$client = new Client([
'base_uri' => 'https://api.coolify.io/v1/',
'headers' => [
'Authorization' => 'Bearer ' . env('COOLIFY_API_TOKEN'),
'Accept' => 'application/json',
],
]);
$response = $client->get('organizations');
$organizations = json_decode($response->getBody(), true);JavaScript (Node.js/Axios)
const axios = require('axios');
const client = axios.create({
baseURL: 'https://api.coolify.io/v1',
headers: {
'Authorization': `Bearer ${process.env.COOLIFY_API_TOKEN}`,
'Accept': 'application/json',
},
});
const organizations = await client.get('/organizations');
console.log(organizations.data);Python (Requests)
import requests
import os
headers = {
'Authorization': f'Bearer {os.getenv("COOLIFY_API_TOKEN")}',
'Accept': 'application/json',
}
response = requests.get('https://api.coolify.io/v1/organizations', headers=headers)
organizations = response.json()
### Rate Limiting Documentation
**File:** `docs/api/rate-limiting.md`
```markdown
# API Rate Limiting
Coolify Enterprise enforces tiered rate limiting based on your organization's license.
## Rate Limit Tiers
| Tier | Requests/Minute | Requests/Hour | Burst Allowance |
|------|-----------------|---------------|-----------------|
| **Starter** | 100 | 5,000 | +20 |
| **Professional** | 500 | 25,000 | +100 |
| **Enterprise** | 2,000 | 100,000 | +500 |
**Burst Allowance**: Temporary extra requests allowed above the per-minute limit.
## Rate Limit Headers
Every API response includes rate limit information:
```http
HTTP/1.1 200 OK
X-RateLimit-Limit: 500
X-RateLimit-Remaining: 487
X-RateLimit-Reset: 1678901234
| Header | Description |
|---|---|
X-RateLimit-Limit |
Maximum requests allowed in current window |
X-RateLimit-Remaining |
Requests remaining before rate limit |
X-RateLimit-Reset |
Unix timestamp when rate limit resets |
Handling Rate Limit Exceeded (429)
When rate limit is exceeded, you'll receive:
HTTP/1.1 429 Too Many Requests
Retry-After: 42
X-RateLimit-Reset: 1678901234
Content-Type: application/json
{
"message": "Too Many Requests"
}Recommended Retry Strategy
async function makeRequestWithRetry(url, options, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(url, options);
if (response.status === 429) {
const retryAfter = parseInt(response.headers.get('Retry-After') || '60');
console.log(`Rate limit exceeded. Waiting ${retryAfter} seconds...`);
await sleep(retryAfter * 1000);
continue;
}
return response;
} catch (error) {
if (i === maxRetries - 1) throw error;
await sleep(Math.pow(2, i) * 1000); // Exponential backoff
}
}
}Optimizing API Usage
- Caching: Cache responses locally when data doesn't change frequently
- Batch Operations: Use bulk endpoints when available (e.g.,
/applications/bulk-deploy) - Webhooks: Subscribe to webhooks instead of polling
- Pagination: Request only the data you need with
per_pageparameter - Conditional Requests: Use
If-None-Matchwith ETags to avoid unnecessary transfers
Enterprise Custom Limits
Enterprise tier organizations can request custom rate limits. Contact support with your use case.
### Code Examples
**File:** `docs/api/examples/php/provision-infrastructure.php`
```php
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
$client = new Client([
'base_uri' => 'https://api.coolify.io/v1/',
'headers' => [
'Authorization' => 'Bearer ' . getenv('COOLIFY_API_TOKEN'),
'Accept' => 'application/json',
],
]);
/**
* Provision AWS infrastructure with 3 t3.medium instances
*/
function provisionInfrastructure(Client $client, int $organizationId): array
{
try {
$response = $client->post('terraform/deployments', [
'json' => [
'organization_id' => $organizationId,
'provider' => 'aws',
'region' => 'us-east-1',
'instance_type' => 't3.medium',
'instance_count' => 3,
'cloud_credential_id' => 7,
],
]);
return json_decode($response->getBody(), true);
} catch (RequestException $e) {
if ($e->hasResponse()) {
$statusCode = $e->getResponse()->getStatusCode();
$body = json_decode($e->getResponse()->getBody(), true);
if ($statusCode === 429) {
$retryAfter = $e->getResponse()->getHeader('Retry-After')[0] ?? 60;
echo "Rate limit exceeded. Retry after {$retryAfter} seconds.\n";
sleep($retryAfter);
return provisionInfrastructure($client, $organizationId); // Retry
}
if ($statusCode === 422) {
echo "Validation error:\n";
print_r($body['errors']);
}
}
throw $e;
}
}
/**
* Poll deployment status until completion
*/
function pollDeploymentStatus(Client $client, int $deploymentId, int $maxAttempts = 60): array
{
for ($i = 0; $i < $maxAttempts; $i++) {
$response = $client->get("terraform/deployments/{$deploymentId}");
$deployment = json_decode($response->getBody(), true);
echo "Status: {$deployment['status']}\n";
if ($deployment['status'] === 'completed') {
return $deployment;
}
if ($deployment['status'] === 'failed') {
throw new Exception("Deployment failed: " . ($deployment['error_message'] ?? 'Unknown error'));
}
sleep(10); // Wait 10 seconds before next poll
}
throw new Exception("Deployment timed out after {$maxAttempts} attempts");
}
// Execute provisioning
$deployment = provisionInfrastructure($client, 42);
echo "Deployment initiated: {$deployment['deployment']['id']}\n";
// Wait for completion
$completedDeployment = pollDeploymentStatus($client, $deployment['deployment']['id']);
echo "Provisioning complete!\n";
echo "Servers created:\n";
print_r($completedDeployment['terraform_output']['servers']);
File: docs/api/examples/javascript/provision-infrastructure.js
const axios = require('axios');
const client = axios.create({
baseURL: 'https://api.coolify.io/v1',
headers: {
'Authorization': `Bearer ${process.env.COOLIFY_API_TOKEN}`,
'Accept': 'application/json',
},
});
/**
* Provision AWS infrastructure
*/
async function provisionInfrastructure(organizationId) {
try {
const response = await client.post('/terraform/deployments', {
organization_id: organizationId,
provider: 'aws',
region: 'us-east-1',
instance_type: 't3.medium',
instance_count: 3,
cloud_credential_id: 7,
});
return response.data;
} catch (error) {
if (error.response?.status === 429) {
const retryAfter = parseInt(error.response.headers['retry-after'] || '60');
console.log(`Rate limit exceeded. Waiting ${retryAfter} seconds...`);
await sleep(retryAfter * 1000);
return provisionInfrastructure(organizationId); // Retry
}
if (error.response?.status === 422) {
console.error('Validation errors:', error.response.data.errors);
}
throw error;
}
}
/**
* Poll deployment status
*/
async function pollDeploymentStatus(deploymentId, maxAttempts = 60) {
for (let i = 0; i < maxAttempts; i++) {
const response = await client.get(`/terraform/deployments/${deploymentId}`);
const deployment = response.data;
console.log(`Status: ${deployment.status}`);
if (deployment.status === 'completed') {
return deployment;
}
if (deployment.status === 'failed') {
throw new Error(`Deployment failed: ${deployment.error_message || 'Unknown error'}`);
}
await sleep(10000); // Wait 10 seconds
}
throw new Error(`Deployment timed out after ${maxAttempts} attempts`);
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// Execute
(async () => {
try {
const deployment = await provisionInfrastructure(42);
console.log(`Deployment initiated: ${deployment.deployment.id}`);
const completed = await pollDeploymentStatus(deployment.deployment.id);
console.log('Provisioning complete!');
console.log('Servers:', completed.terraform_output.servers);
} catch (error) {
console.error('Error:', error.message);
process.exit(1);
}
})();File: docs/api/examples/python/provision-infrastructure.py
import requests
import time
import os
from typing import Dict, Optional
API_BASE = 'https://api.coolify.io/v1'
API_TOKEN = os.getenv('COOLIFY_API_TOKEN')
headers = {
'Authorization': f'Bearer {API_TOKEN}',
'Accept': 'application/json',
}
def provision_infrastructure(organization_id: int) -> Dict:
"""Provision AWS infrastructure"""
payload = {
'organization_id': organization_id,
'provider': 'aws',
'region': 'us-east-1',
'instance_type': 't3.medium',
'instance_count': 3,
'cloud_credential_id': 7,
}
try:
response = requests.post(
f'{API_BASE}/terraform/deployments',
json=payload,
headers=headers
)
response.raise_for_status()
return response.json()
except requests.exceptions.HTTPError as e:
if e.response.status_code == 429:
retry_after = int(e.response.headers.get('Retry-After', 60))
print(f'Rate limit exceeded. Waiting {retry_after} seconds...')
time.sleep(retry_after)
return provision_infrastructure(organization_id) # Retry
if e.response.status_code == 422:
print('Validation errors:', e.response.json().get('errors'))
raise
def poll_deployment_status(deployment_id: int, max_attempts: int = 60) -> Dict:
"""Poll deployment status until completion"""
for i in range(max_attempts):
response = requests.get(
f'{API_BASE}/terraform/deployments/{deployment_id}',
headers=headers
)
response.raise_for_status()
deployment = response.json()
print(f'Status: {deployment["status"]}')
if deployment['status'] == 'completed':
return deployment
if deployment['status'] == 'failed':
error_msg = deployment.get('error_message', 'Unknown error')
raise Exception(f'Deployment failed: {error_msg}')
time.sleep(10) # Wait 10 seconds
raise Exception(f'Deployment timed out after {max_attempts} attempts')
if __name__ == '__main__':
try:
# Provision infrastructure
deployment = provision_infrastructure(42)
print(f'Deployment initiated: {deployment["deployment"]["id"]}')
# Wait for completion
completed = poll_deployment_status(deployment['deployment']['id'])
print('Provisioning complete!')
print('Servers:', completed['terraform_output']['servers'])
except Exception as e:
print(f'Error: {str(e)}')
exit(1)Swagger UI Integration
File: resources/views/api/documentation.blade.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Coolify Enterprise API Documentation</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css">
<style>
body {
margin: 0;
padding: 0;
}
.topbar {
display: none;
}
</style>
</head>
<body>
<div id="swagger-ui"></div>
<script src="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js"></script>
<script src="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-standalone-preset.js"></script>
<script>
window.onload = function() {
const ui = SwaggerUIBundle({
url: "{{ url('/api/openapi.json') }}",
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout",
persistAuthorization: true,
tryItOutEnabled: true,
filter: true,
supportedSubmitMethods: ['get', 'post', 'put', 'patch', 'delete'],
});
window.ui = ui;
}
</script>
</body>
</html>Route: Add to routes/web.php
Route::get('/api/documentation', function () {
return view('api.documentation');
})->name('api.documentation');
Route::get('/api/openapi.json', function () {
return response()->file(storage_path('api-docs/openapi.json'));
})->name('api.openapi');Implementation Approach
Step 1: Enhance OpenAPI Specification (2-3 hours)
- Review auto-generated OpenAPI spec from L5-Swagger
- Add detailed descriptions for all endpoints
- Add request/response examples
- Define all schemas for enterprise models
- Add security schemes and scopes
- Document all error responses with examples
Step 2: Write Core Documentation Files (4-5 hours)
- Create
docs/api/README.mdlanding page - Write
authentication.mdwith step-by-step token generation - Write
rate-limiting.mdwith tier comparison and retry strategies - Write endpoint-specific guides (
organizations.md,white-label.md, etc.) - Write
webhooks.mdwith HMAC validation examples - Write
errors.mdreference with all status codes
Step 3: Create Code Examples (3-4 hours)
- Write PHP examples using Guzzle (5+ examples)
- Write JavaScript/Node.js examples using Axios (5+ examples)
- Write Python examples using Requests library (5+ examples)
- Write cURL examples for all major endpoints (10+ examples)
- Test all code examples against staging API
Step 4: Swagger UI Integration (1-2 hours)
- Create Blade view for Swagger UI
- Add routes for
/api/documentationand/api/openapi.json - Configure Swagger UI with authentication persistence
- Test "Try it out" functionality for all endpoints
- Add custom branding to Swagger UI (optional)
Step 5: Additional Resources (2-3 hours)
- Export Postman collection from OpenAPI spec
- Test Postman collection with all endpoints
- Write migration guide from standard Coolify
- Create pagination guide with cursor/offset examples
- Write security best practices section
Step 6: Documentation Site Setup (Optional, 2-3 hours)
- Set up static documentation generator (VitePress, Docsify, or similar)
- Implement search functionality
- Add dark mode support
- Create responsive navigation
- Deploy documentation site
Step 7: Review and Testing (1-2 hours)
- Technical review by backend team
- Test all code examples
- Verify all links work
- Check for typos and formatting issues
- Validate OpenAPI spec with online validators
Step 8: Publish and Maintain (1 hour)
- Publish documentation to production
- Add documentation links to main application
- Set up changelog for API updates
- Create process for keeping docs in sync with code changes
Test Strategy
Documentation Quality Tests
Manual Testing Checklist:
- All code examples execute successfully
- All links in documentation are valid
- OpenAPI spec validates with Swagger Editor
- Swagger UI loads and displays all endpoints
- "Try it out" functionality works with test tokens
- Rate limit examples match actual API behavior
- Error response examples match actual API responses
- Authentication guide successfully generates tokens
- Postman collection imports and executes successfully
Automated Tests
File: tests/Feature/Documentation/ApiDocumentationTest.php
<?php
use Illuminate\Support\Facades\Storage;
it('serves OpenAPI specification', function () {
$response = $this->get('/api/openapi.json');
$response->assertOk()
->assertHeader('Content-Type', 'application/json');
$spec = json_decode($response->getContent(), true);
expect($spec)->toHaveKeys(['openapi', 'info', 'paths', 'components']);
expect($spec['openapi'])->toBe('3.1.0');
});
it('loads Swagger UI documentation page', function () {
$response = $this->get('/api/documentation');
$response->assertOk()
->assertSee('swagger-ui')
->assertSee('Coolify Enterprise API');
});
it('includes all enterprise endpoints in OpenAPI spec', function () {
$response = $this->get('/api/openapi.json');
$spec = json_decode($response->getContent(), true);
$requiredEndpoints = [
'/organizations',
'/organizations/{id}',
'/terraform/deployments',
'/white-label/config',
'/monitoring/metrics',
'/payments/subscriptions',
'/domains',
];
foreach ($requiredEndpoints as $endpoint) {
expect($spec['paths'])->toHaveKey($endpoint);
}
});
it('includes rate limit response schemas', function () {
$response = $this->get('/api/openapi.json');
$spec = json_decode($response->getContent(), true);
expect($spec['components']['responses'])->toHaveKey('RateLimitExceeded');
expect($spec['components']['schemas'])->toHaveKey('RateLimitHeaders');
});
it('validates OpenAPI spec structure', function () {
$specPath = storage_path('api-docs/openapi.json');
expect(file_exists($specPath))->toBeTrue();
$spec = json_decode(file_get_contents($specPath), true);
// Validate required top-level fields
expect($spec)->toHaveKeys(['openapi', 'info', 'servers', 'paths', 'components']);
// Validate info section
expect($spec['info'])->toHaveKeys(['title', 'version', 'description']);
// Validate security schemes
expect($spec['components']['securitySchemes'])->toHaveKey('sanctum');
});Integration Tests
File: tests/Feature/Documentation/CodeExamplesTest.php
<?php
it('PHP example code is syntactically valid', function () {
$exampleFiles = glob(base_path('docs/api/examples/php/*.php'));
foreach ($exampleFiles as $file) {
$output = shell_exec("php -l {$file} 2>&1");
expect($output)->toContain('No syntax errors detected');
}
});
it('JavaScript example code is syntactically valid', function () {
$exampleFiles = glob(base_path('docs/api/examples/javascript/*.js'));
foreach ($exampleFiles as $file) {
$output = shell_exec("node --check {$file} 2>&1");
expect($output)->toBeEmpty(); // No output means valid
}
});
it('Python example code is syntactically valid', function () {
$exampleFiles = glob(base_path('docs/api/examples/python/*.py'));
foreach ($exampleFiles as $file) {
$output = shell_exec("python3 -m py_compile {$file} 2>&1");
expect($output)->toBeEmpty(); // No output means valid
}
});Definition of Done
- OpenAPI 3.1 specification complete with all 15+ enterprise endpoints
- Detailed descriptions and examples for every endpoint
- All request/response schemas defined
- Authentication guide written with step-by-step token generation
- Rate limiting documentation complete with tier comparison table
- Code examples written in PHP, JavaScript, Python, cURL (20+ total examples)
- All code examples tested and verified working
- Webhook integration guide with HMAC validation examples
- Error handling reference with all HTTP status codes
- Pagination guide with cursor and offset strategies
- Migration guide from standard Coolify to enterprise
- Security best practices section
- Swagger UI deployed at
/api/documentation - "Try it out" functionality tested and working
- Postman collection exported and tested
- All documentation links verified (no broken links)
- Search functionality implemented (if using doc site)
- Dark mode support (if using doc site)
- Mobile-responsive layout verified
- Copy-to-clipboard buttons for code samples
- API changelog created and up-to-date
- Documentation reviewed by backend and frontend teams
- All tests passing (syntax validation, link checking, OpenAPI validation)
- Documentation deployed to production
- Documentation links added to main application UI
- Process established for keeping docs in sync with code
Related Tasks
- Depends on: Task 61 (Enhanced API System) - all documented endpoints must exist first
- Depends on: Task 54 (Rate Limiting) - rate limit tiers must be implemented to document
- Depends on: Task 52 (Sanctum Extensions) - organization-scoped auth must work to document
- Depends on: Task 58 (Swagger UI Integration) - Swagger UI integration provides interactive docs
- Depends on: Task 57 (OpenAPI Spec) - OpenAPI spec is the foundation for all documentation
- Referenced by: All enterprise tasks - comprehensive docs help developers integrate with all features