Skip to content

Write API documentation with interactive examples #193

@johnproblems

Description

@johnproblems

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:

  1. Organization-Scoped Authentication: All endpoints require organization context with Sanctum tokens
  2. Tiered Rate Limiting: Different rate limits based on enterprise license tier (Starter, Professional, Enterprise)
  3. Multi-Tenant Considerations: How to prevent cross-organization data leakage
  4. Webhook Integrations: Payment gateway webhooks, Terraform provisioning status updates
  5. Complex Workflows: Multi-step processes like provisioning infrastructure → deploying applications → configuring domains
  6. 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:

  1. 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
  2. Authentication & Authorization (3 chapters):

    • Sanctum token generation with organization context
    • API key management with scoped abilities
    • Role-based access control (RBAC) for API operations
  3. 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
  4. 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/documentation route (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/documentation route 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/documentation route)

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

  1. Token Rotation: Rotate API tokens every 90 days
  2. Principle of Least Privilege: Grant only necessary abilities
  3. IP Allowlisting: Restrict tokens to known IP ranges (enterprise tier)
  4. Token Expiration: Always set expiration dates
  5. 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

  1. Caching: Cache responses locally when data doesn't change frequently
  2. Batch Operations: Use bulk endpoints when available (e.g., /applications/bulk-deploy)
  3. Webhooks: Subscribe to webhooks instead of polling
  4. Pagination: Request only the data you need with per_page parameter
  5. Conditional Requests: Use If-None-Match with 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)

  1. Review auto-generated OpenAPI spec from L5-Swagger
  2. Add detailed descriptions for all endpoints
  3. Add request/response examples
  4. Define all schemas for enterprise models
  5. Add security schemes and scopes
  6. Document all error responses with examples

Step 2: Write Core Documentation Files (4-5 hours)

  1. Create docs/api/README.md landing page
  2. Write authentication.md with step-by-step token generation
  3. Write rate-limiting.md with tier comparison and retry strategies
  4. Write endpoint-specific guides (organizations.md, white-label.md, etc.)
  5. Write webhooks.md with HMAC validation examples
  6. Write errors.md reference with all status codes

Step 3: Create Code Examples (3-4 hours)

  1. Write PHP examples using Guzzle (5+ examples)
  2. Write JavaScript/Node.js examples using Axios (5+ examples)
  3. Write Python examples using Requests library (5+ examples)
  4. Write cURL examples for all major endpoints (10+ examples)
  5. Test all code examples against staging API

Step 4: Swagger UI Integration (1-2 hours)

  1. Create Blade view for Swagger UI
  2. Add routes for /api/documentation and /api/openapi.json
  3. Configure Swagger UI with authentication persistence
  4. Test "Try it out" functionality for all endpoints
  5. Add custom branding to Swagger UI (optional)

Step 5: Additional Resources (2-3 hours)

  1. Export Postman collection from OpenAPI spec
  2. Test Postman collection with all endpoints
  3. Write migration guide from standard Coolify
  4. Create pagination guide with cursor/offset examples
  5. Write security best practices section

Step 6: Documentation Site Setup (Optional, 2-3 hours)

  1. Set up static documentation generator (VitePress, Docsify, or similar)
  2. Implement search functionality
  3. Add dark mode support
  4. Create responsive navigation
  5. Deploy documentation site

Step 7: Review and Testing (1-2 hours)

  1. Technical review by backend team
  2. Test all code examples
  3. Verify all links work
  4. Check for typos and formatting issues
  5. Validate OpenAPI spec with online validators

Step 8: Publish and Maintain (1 hour)

  1. Publish documentation to production
  2. Add documentation links to main application
  3. Set up changelog for API updates
  4. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    epic:topgunTasks for topguntaskIndividual task

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions