Vardo

API Reference

Complete REST API reference for all Vardo endpoints -- authentication, resources, webhooks and error handling.

Authentication

All API endpoints (except /api/health and /api/v1/github/webhook) require authentication. Vardo supports two authentication methods:

Session cookies

The web dashboard uses session-based authentication managed by Better Auth. Sessions are stored in PostgreSQL, expire after 7 days (configurable) and are refreshed every 24 hours. When using the API from a browser context, the session cookie is sent automatically.

API tokens

Create API tokens from User Settings > API Tokens in the dashboard. Tokens use a vardo_ prefix, are stored as SHA-256 hashes and displayed only once at creation time. Tokens are scoped to a specific organization and user.

curl -s https://vardo.example.com/api/v1/organizations/{orgId}/apps \
  -H "Authorization: Bearer vardo_<your-token>"

When authenticating via token, the org context is derived from the token itself -- no cookie is required. This makes tokens suitable for CI/CD pipelines, scripts and API integrations.

Base URL

All versioned API endpoints follow the pattern:

/api/v1/organizations/{orgId}/...

The orgId is the organization ID (not the slug). You can find it from the organization list endpoint or from the dashboard URL.

Rate limits

Vardo uses a Redis-backed sliding window rate limiter applied per route via the withRateLimit wrapper.

TierLimitWindowIdentityUsed for
auth5 req60sIP / token hashLogin, signup, magic link, passkey
public30 req60sIP / token hashWebhooks, mesh join
mutation60 req60sUser ID / token hashCreate, update, delete operations
read120 req60sUser ID / token hashList, get, stream
admin30 req60sUser ID / token hashAdmin API endpoints
critical10 req60sUser ID / token hashDeploy, rollback

When rate-limited, the API returns HTTP 429 with a Retry-After header indicating seconds until the oldest request in the window ages out. If Redis is unavailable, requests are allowed through -- a Redis outage won't take down the API.

Error format

All error responses follow a consistent shape:

{
  "error": "Human-readable error message"
}
CodeMeaning
400Bad request (validation error)
401Unauthorized (not authenticated)
403Forbidden (no access to this organization)
404Not found
409Conflict (e.g. duplicate name)
429Too many requests (rate limited)
500Internal server error

Endpoints

Health

MethodPathAuthDescription
GET/api/healthNoneBasic health check
GET/api/health/systemNoneSystem-level health
GET/api/v1/admin/healthAdminDetailed health with service status, runtime info, auth methods and feature flags

Organizations

MethodPathDescription
GET/api/v1/organizations/{orgId}Get organization details
PATCH/api/v1/organizations/{orgId}Update organization
DELETE/api/v1/organizations/{orgId}Delete organization

Members

MethodPathDescription
GET/api/v1/organizations/{orgId}/membersList organization members
POST/api/v1/organizations/{orgId}/membersAdd a member
PATCH/api/v1/organizations/{orgId}/members/{userId}Update a member's role
DELETE/api/v1/organizations/{orgId}/members/{userId}Remove a member

Invitations

MethodPathDescription
GET/api/v1/organizations/{orgId}/invitationsList invitations
POST/api/v1/organizations/{orgId}/invitationsInvite a user to the org
DELETE/api/v1/organizations/{orgId}/invitations/{invitationId}Cancel an invitation
POST/api/v1/invitations/acceptAccept an invitation (token-verified)

Projects

MethodPathDescription
GET/api/v1/organizations/{orgId}/projectsList all projects
POST/api/v1/organizations/{orgId}/projectsCreate a project
GET/api/v1/organizations/{orgId}/projects/{projectId}Get project details
PATCH/api/v1/organizations/{orgId}/projects/{projectId}Update a project
DELETE/api/v1/organizations/{orgId}/projects/{projectId}Delete a project

Project environments

MethodPathDescription
GET/api/v1/organizations/{orgId}/projects/{projectId}/environmentsList project environments
POST/api/v1/organizations/{orgId}/projects/{projectId}/environmentsCreate a project environment

Project stats

MethodPathDescription
GET/api/v1/organizations/{orgId}/projects/{projectId}/statsAggregate stats for a project
GET/api/v1/organizations/{orgId}/projects/{projectId}/stats/streamLive project stats (SSE)
GET/api/v1/organizations/{orgId}/projects/{projectId}/stats/historyHistorical project stats

Apps

MethodPathDescription
GET/api/v1/organizations/{orgId}/appsList all apps
POST/api/v1/organizations/{orgId}/appsCreate an app
POST/api/v1/organizations/{orgId}/apps/sortUpdate app sort order
GET/api/v1/organizations/{orgId}/apps/{appId}Get app details
PATCH/api/v1/organizations/{orgId}/apps/{appId}Update an app
DELETE/api/v1/organizations/{orgId}/apps/{appId}Delete an app

Create app body

{
  "displayName": "My App",
  "name": "my-app",
  "description": "Optional description",
  "source": "git",
  "deployType": "dockerfile",
  "gitUrl": "https://github.com/user/repo",
  "gitBranch": "main",
  "containerPort": 3000,
  "autoDeploy": true,
  "generateDomain": true,
  "projectId": "optional-project-id"
}

source -- "git" or "direct"

deployType -- "compose", "dockerfile", "image", "static", "nixpacks" or "railpack"

Additional fields depending on deploy type:

  • image: imageName (required)
  • compose: composeContent (inline YAML)
  • git sources: gitUrl, gitBranch, rootDirectory
  • resource limits: cpuLimit (cores, max 64), memoryLimit (MB, 64-65536), diskWriteAlertThreshold (bytes)
  • volumes: persistentVolumes array of { name, mountPath }
  • ports: exposedPorts array of { internal, external?, protocol?, description? }
  • connection info: connectionInfo array of { label, value, copyRef? }

Deployments

MethodPathDescription
POST/api/v1/organizations/{orgId}/apps/{appId}/deployTrigger a deployment (returns SSE stream)
GET/api/v1/organizations/{orgId}/apps/{appId}/deploy/streamStream deployment logs (SSE)
POST/api/v1/organizations/{orgId}/apps/{appId}/stopStop an app
POST/api/v1/organizations/{orgId}/apps/{appId}/restartRestart an app
POST/api/v1/organizations/{orgId}/apps/{appId}/rollbackRoll back to a previous deployment

Deploy body (optional)

{
  "environmentId": "env_abc123",
  "groupEnvironmentId": "genv_abc123",
  "deployAll": true
}
  • Omit the body or send {} to deploy to the default (production) environment.
  • Set deployAll: true to trigger a project-wide deploy of all apps in the project.
  • The response is a Server-Sent Events stream with log, stage, tier and done events.

Branches

MethodPathDescription
GET/api/v1/organizations/{orgId}/apps/{appId}/branchesList branches for the app's connected repository

Environment variables

MethodPathDescription
GET/api/v1/organizations/{orgId}/apps/{appId}/env-varsList app env vars
POST/api/v1/organizations/{orgId}/apps/{appId}/env-varsSet app env vars
GET/api/v1/organizations/{orgId}/env-varsList org-level shared env vars
POST/api/v1/organizations/{orgId}/env-varsSet org-level shared env vars

Domains

MethodPathDescription
GET/api/v1/organizations/{orgId}/apps/{appId}/domainsList app domains
POST/api/v1/organizations/{orgId}/apps/{appId}/domainsAdd a domain
DELETE/api/v1/organizations/{orgId}/apps/{appId}/domainsRemove a domain
POST/api/v1/organizations/{orgId}/apps/{appId}/domains/primarySet the primary domain
GET/api/v1/organizations/{orgId}/apps/{appId}/domains/healthCheck domain health
GET/api/v1/organizations/{orgId}/domainsList all org domains
POST/api/v1/organizations/{orgId}/domainsAdd an org domain

Environments

MethodPathDescription
GET/api/v1/organizations/{orgId}/apps/{appId}/environmentsList app environments
POST/api/v1/organizations/{orgId}/apps/{appId}/environmentsCreate an environment
GET/api/v1/organizations/{orgId}/apps/{appId}/environments/{envId}Get environment details
PATCH/api/v1/organizations/{orgId}/apps/{appId}/environments/{envId}Update an environment
DELETE/api/v1/organizations/{orgId}/apps/{appId}/environments/{envId}Delete an environment
POST/api/v1/organizations/{orgId}/apps/{appId}/environments/{envId}/cloneClone an environment

Environment types: "production", "staging", "preview".

Monitoring

MethodPathDescription
GET/api/v1/organizations/{orgId}/apps/{appId}/statsCurrent container stats (CPU, memory, network)
GET/api/v1/organizations/{orgId}/apps/{appId}/stats/streamLive stats stream (SSE)
GET/api/v1/organizations/{orgId}/apps/{appId}/stats/historyHistorical stats
GET/api/v1/organizations/{orgId}/apps/{appId}/logsQuery logs (Loki with Docker fallback)
GET/api/v1/organizations/{orgId}/apps/{appId}/logs/streamLive log stream (SSE)
GET/api/v1/organizations/{orgId}/apps/{appId}/containersList containers for an app
GET/api/v1/organizations/{orgId}/apps/{appId}/eventsApp events (SSE -- deploy:complete, etc.)
GET/api/v1/organizations/{orgId}/apps/{appId}/volumesList volumes
GET/api/v1/organizations/{orgId}/apps/{appId}/terminalWebSocket terminal access

Org-level stats

MethodPathDescription
GET/api/v1/organizations/{orgId}/statsAggregate stats for the org
GET/api/v1/organizations/{orgId}/stats/streamLive org stats (SSE)
GET/api/v1/organizations/{orgId}/stats/businessBusiness metrics

Backups

MethodPathDescription
GET/api/v1/organizations/{orgId}/backupsList backup overview
GET/api/v1/organizations/{orgId}/backups/targetsList backup targets
POST/api/v1/organizations/{orgId}/backups/targetsCreate a backup target
GET/api/v1/organizations/{orgId}/backups/targets/{targetId}Get backup target details
PATCH/api/v1/organizations/{orgId}/backups/targets/{targetId}Update a backup target
DELETE/api/v1/organizations/{orgId}/backups/targets/{targetId}Delete a backup target
GET/api/v1/organizations/{orgId}/backups/jobs/{jobId}Get backup job details
PATCH/api/v1/organizations/{orgId}/backups/jobs/{jobId}Update a backup job
POST/api/v1/organizations/{orgId}/backups/jobs/{jobId}/runTrigger a backup job manually
GET/api/v1/organizations/{orgId}/backups/history/{backupId}/downloadDownload a backup archive
POST/api/v1/organizations/{orgId}/backups/history/{backupId}/restoreRestore from a backup

Storage backends: S3, Cloudflare R2, Backblaze B2, SSH/SFTP, local filesystem.

Backup strategies are per-volume: tar for file volumes, pg_dump for Postgres.

Cron jobs

MethodPathDescription
GET/api/v1/organizations/{orgId}/apps/{appId}/cronList cron jobs for an app
POST/api/v1/organizations/{orgId}/apps/{appId}/cronCreate a cron job

Cron job types: "command" (runs inside the container) or "url" (HTTP request).

Tags

MethodPathDescription
GET/api/v1/organizations/{orgId}/tagsList tags
POST/api/v1/organizations/{orgId}/tagsCreate a tag
GET/api/v1/organizations/{orgId}/apps/{appId}/tagsList tags for an app
POST/api/v1/organizations/{orgId}/apps/{appId}/tagsAssign a tag to an app
DELETE/api/v1/organizations/{orgId}/apps/{appId}/tagsRemove a tag from an app

Transfers

MethodPathDescription
POST/api/v1/organizations/{orgId}/apps/{appId}/transferInitiate an app transfer
GET/api/v1/organizations/{orgId}/transfersList incoming/outgoing transfers
PATCH/api/v1/organizations/{orgId}/transfers/{transferId}Accept or reject a transfer

Deploy keys

MethodPathDescription
GET/api/v1/organizations/{orgId}/deploy-keysList deploy keys
POST/api/v1/organizations/{orgId}/deploy-keysCreate a deploy key

API tokens

MethodPathDescription
GET/api/v1/organizations/{orgId}/tokensList API tokens
POST/api/v1/organizations/{orgId}/tokensCreate a new token
DELETE/api/v1/organizations/{orgId}/tokensDelete a token

Notifications

MethodPathDescription
GET/api/v1/organizations/{orgId}/notificationsList notification channels
POST/api/v1/organizations/{orgId}/notificationsCreate a notification channel
PATCH/api/v1/organizations/{orgId}/notifications/{channelId}Update a channel
DELETE/api/v1/organizations/{orgId}/notifications/{channelId}Delete a channel
GET/api/v1/organizations/{orgId}/notifications/streamReal-time notification stream (SSE)

Channel types: email (SMTP, Mailpace, Resend, Postmark), webhook (HTTP POST) and Slack (via webhook URL).

Digest

MethodPathDescription
GET/api/v1/organizations/{orgId}/digestGet digest settings
PATCH/api/v1/organizations/{orgId}/digestUpdate digest settings

Activity log

MethodPathDescription
GET/api/v1/organizations/{orgId}/activitiesList activity entries
MethodPathDescription
GET/api/v1/organizations/{orgId}/searchSearch apps, projects and shared variables

GitHub integration

MethodPathAuthDescription
GET/api/v1/github/installationsSessionList connected GitHub App installations
GET/api/v1/github/reposSessionList repositories (?installationId=...)
POST/api/v1/github/reposSessionCreate a repo via GitHub App
GET/api/v1/github/branchesSessionList branches for a repository
GET/api/v1/github/env-scanSessionScan a repo for environment variable usage
POST/api/v1/github/connectSessionInitiate GitHub App connection
GET/api/v1/github/callbackNoneOAuth callback handler
POST/api/v1/github/webhookNoneGitHub webhook receiver (signature-verified)

Templates

MethodPathDescription
GET/api/v1/templatesList available app templates

DNS check

MethodPathDescription
GET/api/v1/dns-checkCheck DNS resolution for a domain

System alerts

MethodPathDescription
GET/api/v1/system/alertsList active system alerts

Admin endpoints

All admin endpoints require isAppAdmin on the authenticated user.

Admin overview and health

MethodPathDescription
GET/api/v1/admin/overviewSystem overview -- user/app/deployment/template counts with sparklines, resource usage
GET/api/v1/admin/healthService health, runtime info (Next.js version, Node version, uptime, memory), auth methods and feature flags
GET/api/v1/admin/statsAdmin-level system stats
GET/api/v1/admin/stats/streamLive admin stats (SSE)

Admin management

MethodPathDescription
GET/api/v1/admin/usersList all users
GET/api/v1/admin/organizationsList all organizations
GET/api/v1/admin/backup-targetsList all backup targets (system-wide)
GET/api/v1/admin/backupsList all backups (system-wide)
POST/api/v1/admin/docker-prunePrune unused Docker resources
POST/api/v1/admin/dns-checkDNS check (admin context)
GET/api/v1/admin/config/exportExport current config as vardo.yml + vardo.secrets.yml
POST/api/v1/admin/config/importImport a vardo.yml config into system settings

Admin mesh operations

These endpoints are only available when the mesh feature flag is enabled.

MethodPathDescription
GET/api/v1/admin/mesh/peersList connected mesh peers
POST/api/v1/admin/mesh/joinJoin a mesh network
POST/api/v1/admin/mesh/inviteGenerate an invite token for a new peer
POST/api/v1/admin/mesh/cloneClone an app from another instance
POST/api/v1/admin/mesh/promotePromote an app to another instance
POST/api/v1/admin/mesh/pullPull an app's config/volumes from another instance

Mesh peer-to-peer endpoints

These are called instance-to-instance over WireGuard tunnels -- not by end users.

MethodPathDescription
POST/api/v1/mesh/joinAccept a join request from another instance
POST/api/v1/mesh/heartbeatPeer heartbeat
POST/api/v1/mesh/syncSync state with a peer
POST/api/v1/mesh/cloneReceive a cloned app
POST/api/v1/mesh/promoteReceive a promoted app
POST/api/v1/mesh/pullRespond to a pull request

MCP server

Vardo exposes a Model Context Protocol server for AI agent integration at /api/mcp. Authenticate with a Bearer token (API token).

MethodPathDescription
POST/api/mcpMCP JSON-RPC endpoint (stateless, fresh server per request)

Available MCP tools (read-only):

ToolDescription
list-appsList all apps in the org
get-app-statusGet status and details for a specific app
get-app-logsGet recent logs for a specific app
list-projectsList all projects in the org

Webhook format

Vardo receives GitHub webhooks at /api/v1/github/webhook. The webhook secret is verified using HMAC-SHA256 (x-hub-signature-256 header).

Supported events

push -- Triggers auto-deploy for apps that match the repository URL and branch with autoDeploy enabled.

{
  "ok": true,
  "deployments": [
    { "app": "my-app", "deploymentId": "abc123", "success": true }
  ]
}

pull_request -- Creates or destroys preview environments.

  • opened, reopened, synchronize -- Creates a preview project environment for the matching project, deploys all apps and posts preview URLs as a PR comment.
  • closed -- Destroys the preview environment and tears down all containers.
{
  "ok": true,
  "preview": {
    "groupEnvironmentId": "genv_abc123",
    "domains": [
      { "appName": "web", "domain": "pr-42-web.example.com" }
    ],
    "deployed": true
  }
}

All other event types are acknowledged with {"ok": true, "skipped": "<event-type>"}.


Setup endpoints

These endpoints are used during initial setup only and don't require authentication (the setup wizard runs before any user exists).

MethodPathDescription
GET/api/setup/statusCheck if setup is needed
GET/api/setup/progressGet setup wizard progress
POST/api/setup/authConfigure auth settings
POST/api/setup/backupConfigure backup storage
POST/api/setup/emailConfigure email provider
POST/api/setup/feature-flagsSet initial feature flags
POST/api/setup/generalSet general settings
POST/api/setup/githubConfigure GitHub App
POST/api/setup/servicesConfigure infrastructure services
POST/api/setup/sslConfigure SSL/TLS

On this page