The orchestrator is an Express service that listens on ORCHESTRATOR_PORT (default 3000). It is typically not exposed publicly: nginx proxies /webhook/github to it; other endpoints are for health checks and debugging from the server (e.g. curl on the droplet or via SSH).
Base URL: http://localhost:3000 (on the server) or http://SERVER_IP:3000 if the port is forwarded. Replace SERVER_IP with your droplet IP when running curl from your machine (only if port 3000 is reachable; by default it is internal).
OpenAPI / Swagger UI: The API spec is available at GET /openapi.json and interactive docs at GET /api-docs for exploration (e.g. http://localhost:3000/openapi.json, http://localhost:3000/api-docs).
Endpoints
GET /health
Liveness check. Returns basic orchestrator status and uptime.
Request: No body or headers required.
Response (200 OK):
{
"status": "ok",
"timestamp": "2026-02-04T12:00:00.000Z",
"uptime": 3600.5
}
Example (from server):
curl http://localhost:3000/health
Use this to confirm the orchestrator process is running (e.g. in Troubleshooting).
POST /webhook/github
Receives GitHub pull request webhook events. Validates the request signature, then triggers deploy, update, or cleanup.
Headers:
| Header | Required | Description |
|---|
Content-Type | Yes | application/json |
X-Hub-Signature-256 | Yes | HMAC SHA256 of the raw body using your webhook secret |
Body: GitHub pull_request webhook payload (JSON). Relevant fields include action (opened, synchronize, closed, reopened), pull_request, and repository.
Response:
- 200 OK:
{ "status": "ok" } — webhook accepted and processed (or queued).
- 401 Unauthorized:
{ "error": "Invalid signature" } — X-Hub-Signature-256 missing or does not match.
- 500 Internal Server Error:
{ "error": "<message>" } — processing failed (e.g. clone, build, or cleanup error).
Example (from server; replace secret and payload):
curl -X POST http://localhost:3000/webhook/github \
-H "Content-Type: application/json" \
-H "X-Hub-Signature-256: sha256=<hmac-hex>" \
-d '{"action":"opened","pull_request":{...},"repository":{...}}'
In production, GitHub sends requests to http://YOUR_SERVER_IP/webhook/github; nginx proxies to the orchestrator. Do not call this endpoint manually unless you are replaying or testing webhooks with a valid signature.
GET /api/previews
Returns all tracked preview deployments. Useful for debugging: see which deployments exist, their status, ports, and URLs.
Request: No body or headers required.
Response (200 OK):
{
"deployments": [
{
"prNumber": 12,
"repoName": "my-app",
"repoOwner": "my-org",
"projectSlug": "my-org-my-app",
"deploymentId": "my-org-my-app-12",
"branch": "feature-branch",
"commitSha": "abc123...",
"framework": "nestjs",
"dbType": "postgres",
"appPort": 3000,
"exposedAppPort": 8012,
"exposedDbPort": 9012,
"status": "running",
"createdAt": "2026-02-04T10:00:00.000Z",
"updatedAt": "2026-02-04T10:05:00.000Z",
"url": "http://SERVER_IP/my-org-my-app/pr-12/",
"commentId": 456789
}
]
}
Response (500): { "error": "<message>" } — e.g. tracker read failed.
Example (from server):
curl http://localhost:3000/api/previews
DELETE /api/previews/:deploymentId
Manually cleanup a single preview: stops and removes Docker containers, removes nginx config, and deletes the deployment from the tracker. Use for debugging stuck previews or reclaiming resources.
Parameters:
| Parameter | Location | Description |
|---|
deploymentId | path | Full deployment id (e.g. my-org-my-app-12). Format: {projectSlug}-{prNumber}. |
Response:
- 200 OK:
{ "status": "ok", "message": "Preview <deploymentId> cleaned up" }
- 400 Bad Request:
{ "error": "Invalid deployment id" } — missing or empty deploymentId.
- 404 Not Found:
{ "error": "Deployment not found" } — no tracked deployment with that id.
- 500 Internal Server Error:
{ "error": "<message>" } — cleanup failed (Docker, nginx, or tracker).
Example (from server; use a real deploymentId from GET /api/previews):
curl -X DELETE http://localhost:3000/api/previews/my-org-my-app-12
This endpoint immediately tears down the preview. Use it when you need to force-clean a deployment (e.g. after a failed build or for testing). For normal cleanup, rely on PR close or TTL.
Debugging tips
- Orchestrator not responding: Check that the service is running (
journalctl -u preview-orchestrator -f) and that ORCHESTRATOR_PORT (default 3000) is correct. Call GET /health from the server.
- List current previews: Use
GET /api/previews to see all deployments, their deploymentId, status, and exposedAppPort/exposedDbPort. Compare with docker ps and nginx configs.
- Force-clean a preview: If a preview is stuck or you need to free a port, use
DELETE /api/previews/:deploymentId with the id from GET /api/previews. Then verify with GET /api/previews and docker ps.
- Webhook signature: Manual
POST /webhook/github calls must include a valid X-Hub-Signature-256 header (HMAC SHA256 of the raw JSON body with your GITHUB_WEBHOOK_SECRET). See GitHub webhook signature verification.
For more debugging steps, see Troubleshooting.