API reference
Minimal JSON API for customer CI/CD and automation.
Authentication
Generate a token in Settings → API access. Send it on every request:
Authorization: Bearer YOUR_TOKEN
Tokens are stored as SHA-256 digests — the raw token is shown once at generation.
Endpoints
Base path: /api/v1
Account summary
GET /api/v1/account
Returns plan, usage counts, join URL (admin scope only), api_tokens_configured, integration status, alert thresholds, and retention settings.
Account stats
GET /api/v1/stats
Returns app/server counts (including deploying, unhealthy, maintenance), recent deploys, and recent events.
Regenerate team invite link
POST /api/v1/account/regenerate_join_code
Invalidates previous invite URLs. Returns updated account JSON.
Team members
GET /api/v1/users
PATCH /api/v1/users/:id
DELETE /api/v1/users/:id
List includes join_url and team_limit. Update role:
{ "user": { "role": "admin" } }
Roles: owner, admin, member.
Invite team member by email
POST /api/v1/users/invite
{ "email": "teammate@company.com" }
Requires SMTP (MAILBIRD_SMTP_PASSWORD). Queues the same invite email as Settings → Team. Returns { "status": "ok", "email": "..." }. Recorded in Activity under Team.
API tokens
Create tokens in Settings → API access (web UI) or via API (requires admin scope):
GET /api/v1/api_tokens?limit=25&before_id=100
POST /api/v1/api_tokens { "name": "cursor", "scopes": ["read", "deploy"], "expires_in": 2592000 }
DELETE /api/v1/api_tokens/:id
List returns { items, has_more, before_id }. The raw token is returned once on create. Optional expiry: expires_in (seconds from now) or expires_at (ISO8601). Expired tokens fail authentication.
Available scopes
| Scope | Access |
|---|---|
read |
Read account stats, events, team list |
write |
App mutations (exec, env, port, branch) — not deploy hooks, auto-deploy, servers, or admin |
admin |
Team invites, roles, join link, API token management |
deploy |
Deploys, rollbacks, webhooks, maintenance |
servers |
Servers, databases, backups, metrics, provisioning |
env |
Environment variables (read + write) |
processes |
App processes and cron jobs (read + write) |
domains |
App domains, SSL verify, mail domains and mailboxes |
Insufficient scope returns 403 with { "error": "Token missing scope: …" } or { "error": "Token missing read access" }.
Unknown scope names are rejected with 422 on token create. API rate limits are per token (120/min global) with additional hourly caps on sensitive actions:
| Action | Limit |
|---|---|
App deploy, exec |
10/minute |
App destroy, rollback, redeploy |
5/hour |
App failover, wake, sleep, scale_web, scale_worker, deprovision_web_node, deprovision_worker_node, provision_standby, sync_hosted_dns, clone, transfer |
5/hour |
App regenerate_webhook_secret |
5/hour |
App test_hook, test_webhook |
10/hour |
App env_export, env import, env bulk_copy |
10/hour |
| Bulk deploy | 5/hour |
Domain verify, sync_dns; mail sync_dns |
10/hour |
Database boot, reboot |
10/hour |
Database destroy |
3/hour |
Process sync / restart; cron sync |
5/hour |
Process / cron destroy |
10/hour |
Webhook delivery replay |
10/hour |
Mail domain create/destroy; mail sync_dns |
5/hour |
| Mailbox create/destroy | 10/hour |
| API token create | 5/hour |
| Team invite | 10/hour |
| Join code regenerate | 3/hour |
apply_manifest |
5/hour |
Manifest read (manifest, manifest_preview) |
20/hour |
Server destroy, provision, bootstrap, purge_backups, assign_floating_ip |
5/hour |
Server reboot |
3/hour |
Server collect_metrics |
10/hour |
Backup create |
10/hour |
Backup restore |
3/hour |
Backup download |
20/hour |
Pipeline promote |
10/hour |
| Platform cert ensure | 2/hour |
Example CI token (deploy only):
{ "name": "github-actions", "scopes": ["read", "deploy"] }
List apps
GET /api/v1/projects?server_id=1&health_status=unhealthy&limit=25&before_id=100
Returns { items, has_more, before_id }. limit max 100.
Bulk deploy
POST /api/v1/projects/bulk_deploy
{ "project_ids": [1, 2, 3], "branch": "main" }
Queues deploys for up to 10 apps. Returns { queued: [...], skipped: [...] } with per-app reasons when blocked. Requires deploy scope. Rate limited to 5/hour per token.
Show app
GET /api/v1/projects/:id
Includes latest_deployment summary.
Create app
POST /api/v1/projects
Body:
{
"project": {
"name": "my-app",
"server_id": 1,
"git_url": "https://github.com/org/app.git",
"branch": "main",
"framework": "rails",
"deploy_strategy": "kamal",
"port": 3000
}
}
Returns 201 Created or 422 with validation errors.
No git OAuth required — use a public HTTPS git_url, or embed a read-only token in the URL for private repos. Set "auto_deploy": false unless git is connected in the panel.
Create app from upload
POST /api/v1/projects/upload
Requires write scope. Send multipart/form-data (not JSON):
| Field | Required | Description |
|---|---|---|
source_archive |
yes | .zip or .tar.gz of your project |
name |
no | App name (inferred from filename if omitted) |
deploy |
no | Queue first deploy (default true) |
Example:
curl -X POST "https://tycoonbox.io/api/v1/projects/upload" \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "source_archive=@app.zip" \
-F "name=my-app" \
-F "deploy=true"
Returns 201 Created with project JSON (source_kind: "upload", upload_filename, optional latest_deployment). Requires deploy scope when deploy=true.
Replace upload archive
POST /api/v1/projects/:id/replace_upload
Requires write scope; add deploy scope when deploy=true (default). Multipart field source_archive (.zip or .tar.gz). Replaces the stored archive and optionally queues a deploy.
curl -X POST "https://tycoonbox.io/api/v1/projects/42/replace_upload" \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "source_archive=@app-v2.zip" \
-F "deploy=true"
Git create — deploy after create
POST /api/v1/projects accepts optional query/body flag deploy_now=true (default). Queues a first deploy when the token includes deploy scope — no git OAuth required for public HTTPS URLs.
Trigger deploy
POST /api/v1/projects/:id/deploy
Optional preview branch (does not change app config):
{ "branch": "feature/checkout" }
Returns 202 Accepted with deployment JSON, or 409 if a deploy is already running.
Cancel queued deploy
POST /api/v1/projects/:id/cancel_deploy
Cancels the active queued or running deploy.
Or cancel by deployment id:
POST /api/v1/deployments/:id/cancel
Rollback
POST /api/v1/projects/:id/rollback
Runs managed or native rollback synchronously. Returns { "status": "ok" }.
Config redeploy (managed apps)
POST /api/v1/projects/:id/redeploy
Pushes updated env vars and secrets without rebuilding the app image.
Run command
POST /api/v1/projects/:id/exec
Body:
{ "command": "bin/rails db:migrate" }
Returns { "project_id": 1, "output": "...", "duration_ms": 842, "ran_at": "2026-09-15T00:00:00Z" }. Records a command_executed event with token audit metadata.
Command execution history
GET /api/v1/projects/:id/command_executions?limit=25&before_id=100
Returns { items, has_more, before_id }. Each item includes command, duration_ms, ran_at, and optional api_token_id / api_token_name. Requires deploy scope.
App logs
GET /api/v1/projects/:id/logs?lines=200&since=10m&drain=true
GET /api/v1/projects/:id/logs_stream?lines=200
`logs_stream` returns `text/event-stream` with `event: log` payloads (JSON-encoded line text). The panel logs page uses this instead of JSON polling.
Query params:
| Param | Description |
|---|---|
lines |
Max lines to tail (default 200, max 500) |
since |
Runtime log time filter (e.g. 10m, 1h, 2026-09-01T12:00:00) |
drain |
When true, forward the tailed output to the app's log_drain_url (if configured) |
Returns { "project_id": 1, "lines": "...", "captured_at": "2026-09-15T00:00:00Z", "drained": true }. drained is true only when drain=true and a log drain URL is set.
Health check
GET /api/v1/projects/:id/health
Runs the configured health probe (HTTP to health_check_path) and returns:
{
"healthy": true,
"health_status": "healthy",
"health_error": null,
"checked_at": "2026-09-09T12:00:00Z",
"auto_failover": false,
"health_failure_count": 0,
"failover_threshold": 3
}
When auto_failover is enabled and a standby server is configured, three consecutive failed checks (~15 minutes via the cron job) trigger automatic promotion via Projects::FailoverPromoter.
Failover to standby
POST /api/v1/projects/:id/failover
Promotes the configured standby server to primary and queues a deploy. Returns { "status": "ok", "new_server_id": 2, "deployment_id": 42 }. Rate limited to 5/hour per token.
Sync hosted DNS
POST /api/v1/projects/:id/sync_hosted_dns
Syncs Route53 records for all hosted domains on the app. Returns per-domain results:
{
"status": "ok",
"synced": ["myapp.tycoonbox.app"],
"failed": [],
"domains": []
}
status is ok (all synced), partial (some failed), or failed (none synced). Returns 422 when every domain fails. Records a hosted_dns_synced event with token audit metadata. Rate limited to 5/hour per token. Requires servers scope.
Test deploy webhook
POST /api/v1/projects/:id/test_webhook
Sends a sample GitHub/GitLab/Bitbucket payload to the app's webhook URL.
Deployment status + logs
GET /api/v1/deployments/:id
Returns deploy log text for polling from CI. Log output is capped at 64KB in API responses; live deploy logs are retained up to 512KB server-side.
List deploy history
GET /api/v1/projects/:id/deployments?limit=25&before_id=100
Returns { items, has_more, before_id } for cursor pagination (newest first). limit max 100.
List servers
GET /api/v1/servers?limit=25&before_id=100
Returns { items, has_more, before_id } for cursor pagination (newest first). limit max 100.
Show server
GET /api/v1/servers/:id
Includes linked apps.
Create server
POST /api/v1/servers
{
"server": {
"name": "production",
"provider": "hetzner",
"region": "nbg1",
"server_type": "cx22"
}
}
For manual servers include "provider": "manual" and "ip_address".
Update server
PATCH /api/v1/servers/:id
{ "server": { "name": "prod-1", "ssh_port": 22 } }
Server firewall
GET /api/v1/servers/:id/firewall
Returns UFW status via SSH.
Collect metrics snapshot
POST /api/v1/servers/:id/collect_metrics
SSH probe + stores snapshot + runs alert checks.
Server metrics
GET /api/v1/servers/:id/metrics
Returns load average, memory, and disk usage via SSH.
Server metrics history
GET /api/v1/servers/:id/metrics_history?limit=48&before_id=100
Returns { items, has_more, before_id } for cursor pagination. limit defaults to 48, max 168 (7 days at 15-minute intervals). Snapshots are retained 7 days.
Provision server
POST /api/v1/servers/:id/provision
Returns 202 Accepted for cloud servers or marks manual servers active.
Bootstrap server
POST /api/v1/servers/:id/bootstrap
Returns 202 Accepted. Requires SSH key in account settings.
Reboot server
POST /api/v1/servers/:id/reboot
Sends reboot over SSH. Requires SSH key in account settings.
Delete server
DELETE /api/v1/servers/:id?confirm=true
Requires confirm=true. Removes the server record and deprovisions cloud VMs when applicable. Blocked when apps (including cluster nodes), databases, or standby assignments remain. On app blockers, the 422 response includes blocking_apps with each app's id, name, role (primary, web, or worker), and optional process_name. Rate limited to 5/hour per token.
Purge old backups
POST /api/v1/servers/:id/purge_backups
Deletes backup files on the server older than the account's backup_retention_days setting.
Assign floating IP
POST /api/v1/servers/:id/assign_floating_ip
Creates or reassigns a floating IP on the server and copies it to linked apps' load_balancer_ip when unset. Requires a cloud API token and a server provisioned via cloud.
Returns { "status": "ok", "floating_ip": "203.0.113.10", "cloud_floating_ip_id": "12345" }.
Update app
PATCH /api/v1/projects/:id
Body (any of):
{
"project": {
"branch": "main",
"port": 3000,
"health_check_path": "/up",
"release_command": "bundle exec rails db:migrate",
"auto_deploy": true,
"load_balancer_ip": "203.0.113.10",
"standby_server_id": 2,
"auto_failover": true,
"auto_floating_ip": true
}
}
When auto_floating_ip is enabled, Tycoonbox provisions or transfers a floating IP to the primary server on save and during failover.
When auto_load_balancer is enabled (requires 2+ cloud web nodes in the same cluster), Tycoonbox creates a cloud load balancer, syncs targets on cluster edits, and sets load_balancer_ip to the LB public IP. After a custom domain is DNS-verified (ssl_status: active), the load balancer gets HTTPS termination on port 443 with HTTP→HTTPS redirect. Mutually exclusive with auto_floating_ip.
Delete app
DELETE /api/v1/projects/:id?confirm=true
Requires confirm=true. Tears down review apps, git webhooks, remote managed/native resources, then deletes the app record. Rate limited to 5/hour per token.
Activity feed
GET /api/v1/events?limit=25&before_id=100&event_action=deploy_succeeded&eventable_type=Project&since=2026-09-01T00:00:00Z
Recent account events (deploys, backups, alerts, config changes). Filter by event_action, eventable_type, or since (ISO8601). Returns { items, has_more, before_id } for cursor pagination. limit max 100.
App domains
GET /api/v1/projects/:project_id/domains?limit=25&before_id=100
POST /api/v1/projects/:project_id/domains
POST /api/v1/projects/:project_id/domains/:id/verify
DELETE /api/v1/projects/:project_id/domains/:id?confirm=true
List returns { items, has_more, before_id }. Delete requires confirm=true and records API token audit.
Create body:
{ "domain": { "hostname": "app.example.com", "primary": true } }
List server databases
GET /api/v1/servers/:server_id/databases?limit=25&before_id=100
Returns { items, has_more, before_id } for cursor pagination. limit max 100.
connection_url is omitted unless the token has write scope; read-only tokens get credentials_redacted: true on each database record.
Create database
POST /api/v1/servers/:server_id/databases
{ "database": { "name": "myapp_production", "engine": "postgresql", "project_id": 1, "backup_schedule": "daily" } }
SQLite uses a host volume mounted at /rails/storage — assign to an app and deploy; no accessory boot step. DATABASE_URL is injected automatically (e.g. sqlite3:/rails/storage/myapp_production.sqlite3).
Update database
PATCH /api/v1/servers/:server_id/databases/:id
{ "database": { "backup_schedule": "weekly", "project_id": 2 } }
Delete database
DELETE /api/v1/servers/:server_id/databases/:id?confirm=true
Requires confirm=true (safety gate). Removes the database record and deprovisions managed database services when booted. Rate limited to 3/hour per token.
Boot / reboot database accessory
POST /api/v1/servers/:server_id/databases/:id/boot
POST /api/v1/servers/:server_id/databases/:id/reboot
Requires the database assigned to a managed (container) app.
List backups
GET /api/v1/servers/:server_id/databases/:database_id/backups?limit=25&before_id=100
Returns { items, has_more, before_id }. limit max 100.
Create backup
POST /api/v1/servers/:server_id/databases/:database_id/backups
Returns 202 Accepted with backup JSON. Supported engines: postgresql, mysql, redis, sqlite.
Backup status
GET /api/v1/backups/:id
Download backup
GET /api/v1/backups/:id/download
Streams the .sql.gz file from the server.
Restore backup
POST /api/v1/backups/:id/restore
Requires confirm=true (safety gate). Optional target database on the same server:
{ "confirm": true, "target_database_id": 2 }
Returns 202 Accepted. Overwrites live data in the target database. Rate limited to 3/hour per token.
Transfer app to another account
POST /api/v1/projects/:id/transfer?confirm=true
{
"target_join_code": "abc123",
"target_server_id": 2
}
Moves the app (env vars, processes, domains) to another account. Requires admin scope and confirm=true. Target is identified by team join code (Settings → Team). The target server must belong to that account. Blocked while deploying, when databases remain on other servers, or when the app is in a pipeline. Clears cluster assignments, webhooks, and standby config — redeploy required on the target server. Rate limited to 5/hour per token.
Clone app
POST /api/v1/projects/:id/clone
Body:
{ "name": "my-app-staging", "server_id": 1 }
Copies env vars and process definitions from the source app. Requires write + env scope (clone duplicates secrets). Respects account app limits. Rate limited to 5/hour per token. Records app_cloned with token audit metadata.
Maintenance mode
POST /api/v1/projects/:id/maintenance
Body:
{ "enabled": true }
Environment variables
GET /api/v1/projects/:project_id/environment_variables?limit=25&before_id=100&q=SECRET
POST /api/v1/projects/:project_id/environment_variables
PATCH /api/v1/projects/:project_id/environment_variables/:id
DELETE /api/v1/projects/:project_id/environment_variables/:id
List returns { items, has_more, before_id } for cursor pagination. Filter keys with q (case-insensitive substring). limit max 100. Values are never returned — only whether a value is set.
Create body:
{ "environment_variable": { "key": "RAILS_MASTER_KEY", "value": "..." } }
Update body:
{ "environment_variable": { "value": "..." } }
Bulk import (.env format — comments and quoted values supported):
POST /api/v1/projects/:project_id/environment_variables/import
Body (either form):
{ "env": "RAILS_MASTER_KEY=abc\nDATABASE_URL=postgres://..." }
Or:
{
"environment_variables": [
{ "key": "RAILS_MASTER_KEY", "value": "abc" },
{ "key": "DATABASE_URL", "value": "postgres://..." }
]
}
Returns { "imported": 2, "skipped": 0, "errors": [] }. Payload limits: 64KB, 500 lines, 200 keys (oversize returns 413 or 422).
Formation (Heroku ps:scale)
GET /api/v1/projects/:project_id/formation
PATCH /api/v1/projects/:project_id/formation?sync=true
Show returns { "project_id", "formation": { "web": 1, "worker": 2 }, "dyno_count": 3 } — dyno counts per non-cron process. Requires read plus processes or deploy scope.
Update accepts a flat JSON body (Heroku-style) or a wrapped formation object:
{ "web": 2, "worker": 1 }
Or { "formation": { "web": { "quantity": 2 } } }. Optional sync query param (default true) enqueues a process sync after scaling. Returns { "project_id", "formation", "changed", "sync_enqueued" }. Requires processes write scope. Rate limited to 20/hour.
Scaling up on cloud primaries auto-queues scale_web / scale_worker jobs when host capacity is short; scaling down deprovisions surplus nodes.
CLI equivalent: tycoonbox ps:scale APP web=2 worker=1
App processes
GET /api/v1/projects/:project_id/app_processes?limit=25&before_id=100
POST /api/v1/projects/:project_id/app_processes
PATCH /api/v1/projects/:project_id/app_processes/:id
DELETE /api/v1/projects/:project_id/app_processes/:id?confirm=true
POST /api/v1/projects/:project_id/app_processes/sync
POST /api/v1/projects/:project_id/app_processes/:id/restart
List returns { items, has_more, before_id } (cron jobs excluded). Each process supports instances (1–10, default 1) for web/worker dyno-style scaling. Managed deploys run one instance per host — when host_shortfall > 0, Tycoonbox auto-queues scale_web jobs on cloud primaries (web and worker processes). Cron jobs always use 1 instance.
Scale web nodes
POST /api/v1/projects/:id/scale_web?count=2
Optional count (1–10, default 1) provisions multiple cloud web nodes in one request. Returns { "status": "queued", "message": "...", "count": 2 }. Requires servers scope. Rate limited to 5/hour per token.
Lowering web instances on a cloud app auto-queues deprovision of surplus extra web nodes.
Scale worker nodes
POST /api/v1/projects/:id/scale_worker?process_name=worker&count=2
Required process_name (must match a worker process). Optional count (1–10, default 1) provisions a dedicated worker node when missing, then queues extra web nodes for overflow capacity. Returns { "status": "queued", "process_name": "worker", "count": 2, "worker_nodes": 1, "web_nodes": 1 }. Requires servers scope.
Deprovision cluster nodes
POST /api/v1/projects/:id/deprovision_web_node?server_id=123
POST /api/v1/projects/:id/deprovision_worker_node?process_name=worker&server_id=456
Manually remove an extra web node or dedicated worker node (cannot remove the primary server). Queues deprovision and redeploy. Returns { "status": "queued", "server_id": … }. Requires servers scope. Scaling formation or instances down also auto-queues surplus deprovisions.
Process autoscale
Web and worker processes support optional metric autoscaling (evaluated after each metrics collection cycle, ~15 min):
| Field | Default | Description |
|---|---|---|
autoscale_enabled |
false |
Enable CPU/memory-based scaling |
autoscale_min_instances |
1 |
Floor |
autoscale_max_instances |
10 |
Ceiling |
autoscale_up_pct |
70 |
Scale up when avg CPU or memory exceeds this |
autoscale_down_pct |
30 |
Scale down when avg CPU and memory are below this |
autoscale_requests_up_rpm |
0 |
Scale up when avg requests/min exceeds this (0 = off) |
autoscale_requests_down_rpm |
0 |
Scale down when avg requests/min is below this (0 = off) |
Included in process create/update JSON. Records process_autoscaled events (includes requests_rpm when request thresholds are set).
First-party resources (marketplace)
GET /api/v1/resource_catalog
GET /api/v1/projects/:project_id/resources
POST /api/v1/projects/:project_id/resources
PATCH /api/v1/projects/:project_id/resources/:id
DELETE /api/v1/projects/:project_id/resources/:id?confirm=true
Catalog returns { items: [{ key, engine, kind, label, description, env_key, backup_schedule, manifest_keys, plans? }] } for Postgres, MySQL, Redis, and Mail — all first-party, no third-party add-ons.
Database entries include plan tiers (hobby, standard) with memory limits. Free accounts are limited to hobby.
Attach body: { "engine": "mysql", "plan": "standard" } or { "engine": "mail" }. Resize body: { "plan": "standard" } — updates memory limits and queues a database service reboot when booted. List returns databases and attached mailboxes (kind: database | mail). Create/update/destroy require deploy scope. Connection URLs and SMTP secrets are injected on deploy.
Manifest plan changes on existing resources are applied via Apply manifest (preview shows plan diffs).
Delete requires confirm=true. Rate limited to 10/hour per token.
Bulk env copy (requires write scope — env-only tokens cannot copy secrets):
POST /api/v1/projects/:project_id/environment_variables/bulk_copy
{ "source_project_id": 2, "overwrite": false, "keys": ["RAILS_MASTER_KEY"] }
Rate limited to 10/hour per token. Max 200 keys per copy (same as env import).
Process sync and restart are rate limited to 5/hour per token.
Create body:
{
"app_process": {
"name": "worker",
"kind": "worker",
"command": "bundle exec sidekiq"
}
}
Kinds: web, worker (cron jobs use the dedicated endpoint below).
Cron jobs
Hatchbox-compatible scheduled tasks. Backed by AppProcess records with kind: cron.
GET /api/v1/projects/:project_id/cron_jobs?limit=25&before_id=100
POST /api/v1/projects/:project_id/cron_jobs
PATCH /api/v1/projects/:project_id/cron_jobs/:id
DELETE /api/v1/projects/:project_id/cron_jobs/:id?confirm=true
POST /api/v1/projects/:project_id/cron_jobs/sync
List returns { items, has_more, before_id }. Delete requires confirm=true. Rate limited to 10/hour per token.
Create body:
{
"cron_job": {
"name": "nightly-report",
"schedule": "0 2 * * *",
"command": "bin/rails reports:nightly"
}
}
Response includes schedule_label (human-readable preset when matched).
Export environment
GET /api/v1/projects/:id/env_export
Returns { "env": "KEY=value\n..." } with secrets included. Requires write scope (read+env tokens cannot export). Rate limited to 10/hour per token.
App metrics and usage
GET /api/v1/projects/:id/metrics?history_limit=96
GET /api/v1/projects/:id/usage
metrics returns live runtime stats, request/error counts, and recent snapshots (managed apps). history_limit defaults to 96, max 168. usage returns { formation, dyno_count, container_hours, requests_24h, bandwidth_mb_24h, period_start, period_end }. Requires read plus deploy, processes, or domains scope.
Repo manifest
GET /api/v1/projects/:id/manifest
GET /api/v1/projects/:id/manifest_preview
POST /api/v1/projects/:id/apply_manifest
Read and preview tycoonbox.json from the connected git repo. apply_manifest validates formation/env/resources, applies changes, records manifest_applied, and returns { status, warnings }. Requires deploy scope. Git-heavy reads rate limited to 20/hour; apply to 5/hour per token.
Deploy-config PATCH fields (auto_deploy, release_command, hooks, build_script) require deploy scope even when the token has write.
Renaming (name) is blocked while the app is deploying (409). Successful renames record app_renamed and return rename_notice for managed apps (slug/service change — redeploy recommended).
Activity feed
GET /api/v1/activity?project_id=1&limit=25&before=2026-09-14T12:00:00.000Z&deployments=true
Unified timeline of deploys and account events. Filter with event_action, eventable_type, or pass before (ISO8601) to page older items. Returns { items, has_more, before }. Requires read. limit max 100.
Pipelines
GET /api/v1/pipelines?limit=25&before_id=100
GET /api/v1/pipelines/:id
POST /api/v1/pipelines { "project_id": 1 }
PATCH /api/v1/pipelines/:id { "auto_promote": true }
POST /api/v1/pipelines/:id/promote?confirm=true { "stage_id": 2 }
Index returns { items, has_more, before_id }. show includes per-stage promotion metadata: promotable, blocked_reason, source_commit_sha, target_latest_commit_sha, and already_on_target.
Staging → production promotion workflows. promote requires confirm=true and records API token audit on pipeline_promote_queued. Requires deploy scope for mutations.
Platform TLS
GET /api/v1/platform
POST /api/v1/platform/ensure_letsencrypt_certificate
POST /api/v1/platform/ensure_wildcard_certificate
Hosted-domain TLS status and certificate provisioning (Route53/ACM). Requires servers scope to read; admin + servers for cert mutations (2/hour per token).
Webhook delivery log
GET /api/v1/projects/:id/webhook_deliveries?limit=50&before_id=100
POST /api/v1/projects/:id/webhook_deliveries/:delivery_id/replay
Recent git webhook attempts (accepted, branch mismatch, duplicate, etc.). Returns { items, has_more, before_id }; each item includes replayable for statuses disabled, branch_mismatch, blocked, and conflict.
Replay re-queues a deploy on the app's configured branch (useful after fixing auto-deploy settings or branch config). Requires deploy scope. Rate limited to 10/hour per token.
Cluster assignment
Requires servers scope.
PATCH /api/v1/projects/:id/web_hosts
{ "web_server_ids": [2, 3] }
Adds extra web nodes (excluding the primary server). Returns web_hosts, web_server_ids, and worker_assignments.
PATCH /api/v1/projects/:id/worker_hosts
{ "worker_server_assignments": { "sidekiq": 4, "jobs": 5 } }
Maps worker process names to dedicated servers. Omit or set 0 to run on the primary server.
Deploy config export
GET /api/v1/projects/:id/kamal_config
Returns generated deploy.yml for managed (container) apps.
Regenerate git webhook secret
POST /api/v1/projects/:id/regenerate_webhook_secret
Returns new webhook_url for GitHub/GitLab/Bitbucket auto-deploy.
Deploy hooks
Set via PATCH /api/v1/projects/:id:
{
"project": {
"pre_deploy_hook": "bin/rails assets:precompile",
"post_deploy_hook": "bin/rails db:migrate"
}
}
Managed apps only. Panel scripts override repo .tycoonbox/hooks/ when set. Hooks notify the panel via deploy callback URL.
Build scripts and tool versions
Native deploys install mise during server bootstrap. When the repo contains .tool-versions, .ruby-version, or .node-version, deploy runs mise install before building.
Set a custom build via PATCH /api/v1/projects/:id:
{
"project": {
"build_script": "bundle install\nnpm run build"
}
}
Priority: repo .tycoonbox/build → panel build_script → framework defaults (Rails/Hanami/Node).
Managed deploys log detected tool versions and apply Ruby/Node version hints from the repo when present.
Frameworks: rails, hanami, node. Auto-detect also recognizes Astro (Node SSR) and Sidekiq workers.
Test deploy hook
POST /api/v1/projects/:id/test_hook
Body:
{ "phase": "pre-deploy" }
Runs the hook script over SSH (without deploying). Phase: pre-deploy or post-deploy. Returns { "output": "..." }. Requires SSH key in account settings.
Deployment detail (GET /api/v1/deployments/:id) includes hook_callback_url for hook scripts to report progress.
Mail domains
GET /api/v1/mail_domains?limit=25&before_id=100
GET /api/v1/mail_domains/:id
POST /api/v1/mail_domains
DELETE /api/v1/mail_domains/:id
POST /api/v1/mail_domains/:id/sync_dns
List returns { items, has_more, before_id }.
Create body:
{ "mail_domain": { "name": "example.com" } }
Show includes DNS record hints and mailboxes. Delete requires confirm=true and deprovisions mailcow + all mailboxes.
Mailboxes
GET /api/v1/mail_domains/:mail_domain_id/mailboxes?limit=25&before_id=100
POST /api/v1/mail_domains/:mail_domain_id/mailboxes
DELETE /api/v1/mail_domains/:mail_domain_id/mailboxes/:id?confirm=true
List returns { items, has_more, before_id }. Mailbox delete requires confirm=true.
{
"mailbox": {
"local_part": "hello",
"display_name": "Hello",
"purpose": "app",
"quota_mb": 1024,
"project_id": 1
}
}
Examples
TOKEN="your-token"
HOST="https://panel.example.com"
curl -s -H "Authorization: Bearer $TOKEN" "$HOST/api/v1/projects"
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"project":{"name":"api-app","server_id":1,"git_url":"https://github.com/org/app.git"}}' \
"$HOST/api/v1/projects"
curl -s -X POST -H "Authorization: Bearer $TOKEN" "$HOST/api/v1/projects/1/deploy"
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"branch":"staging"}' \
"$HOST/api/v1/projects/1/deploy"
curl -s -X POST -H "Authorization: Bearer $TOKEN" "$HOST/api/v1/projects/1/rollback"
curl -s -X POST -H "Authorization: Bearer $TOKEN" "$HOST/api/v1/projects/1/redeploy"
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"command":"bin/rails db:migrate"}' \
"$HOST/api/v1/projects/1/exec"
curl -s -H "Authorization: Bearer $TOKEN" "$HOST/api/v1/servers/1/metrics"
curl -s -H "Authorization: Bearer $TOKEN" "$HOST/api/v1/servers/1/databases"
curl -s -X POST -H "Authorization: Bearer $TOKEN" "$HOST/api/v1/servers/1/databases/2/backups"
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"target_database_id":2}' \
"$HOST/api/v1/backups/5/restore"
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"name":"api-staging"}' \
"$HOST/api/v1/projects/1/clone"
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"enabled":true}' \
"$HOST/api/v1/projects/1/maintenance"
curl -s -H "Authorization: Bearer $TOKEN" "$HOST/api/v1/deployments/42"
curl -s -H "Authorization: Bearer $TOKEN" "$HOST/api/v1/projects/1/logs"
curl -s -H "Authorization: Bearer $TOKEN" "$HOST/api/v1/servers"
curl -s -H "Authorization: Bearer $TOKEN" "$HOST/api/v1/projects/1/environment_variables"
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"environment_variable":{"key":"API_SECRET","value":"abc"}}' \
"$HOST/api/v1/projects/1/environment_variables"
curl -s -H "Authorization: Bearer $TOKEN" "$HOST/api/v1/projects/1/health"
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"env":"FOO=bar\nBAZ=qux"}' \
"$HOST/api/v1/projects/1/environment_variables/import"
curl -s -H "Authorization: Bearer $TOKEN" "$HOST/api/v1/projects/1/app_processes"
curl -s -X POST -H "Authorization: Bearer $TOKEN" "$HOST/api/v1/servers/1/reboot"
curl -s -X PATCH -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"project":{"branch":"staging"}}' \
"$HOST/api/v1/projects/1"
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"database":{"name":"myapp_production","engine":"postgresql","backup_schedule":"daily"}}' \
"$HOST/api/v1/servers/1/databases"
curl -s -H "Authorization: Bearer $TOKEN" "$HOST/api/v1/events?event_action=deploy_succeeded&limit=10"
curl -s -H "Authorization: Bearer $TOKEN" "$HOST/api/v1/account"
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"server":{"name":"staging","provider":"manual","ip_address":"203.0.113.10"}}' \
"$HOST/api/v1/servers"
curl -s -H "Authorization: Bearer $TOKEN" "$HOST/api/v1/projects/1/env_export"
curl -s -H "Authorization: Bearer $TOKEN" "$HOST/api/v1/mail_domains"
curl -s -H "Authorization: Bearer $TOKEN" "$HOST/api/v1/users"
curl -s -X POST -H "Authorization: Bearer $TOKEN" "$HOST/api/v1/account/regenerate_join_code"
curl -s -H "Authorization: Bearer $TOKEN" "$HOST/api/v1/projects?health_status=unhealthy"
curl -s -H "Authorization: Bearer $TOKEN" "$HOST/api/v1/stats"
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"phase":"pre-deploy"}' \
"$HOST/api/v1/projects/1/test_hook"
curl -s -X POST -H "Authorization: Bearer $TOKEN" "$HOST/api/v1/servers/1/purge_backups"
Database accessories
Use the panel (Server → database → Boot / Reboot / Remove) or wait for the next app deploy to boot new databases automatically.
Successful backups can be downloaded from the server page or via the panel backup show URL. PostgreSQL, MySQL, and SQLite backups can be restored from the server page (overwrites live data). Redis backups are download-only.