Skip to Content
ReferenceREST API Reference

REST API Reference

Base URL

https://gateway.vmkit.dev

Authentication

All /api/* routes except /api/auth/* and /api/webhooks/* require a Bearer token.

Authorization: Bearer vmk_your_key_here

API keys are created and revoked at Settings → API Keys in the dashboard, or via the /api/api-keys endpoints below. Tokens are prefixed vmk_ and shown exactly once on creation.

The token shown at creation time is the only time the full secret is available. Store it immediately — VMKit stores only a hash.


Auth

GET /health

Liveness probe. No auth required.

Response 200:

{ "status": "ok" }

GET /api/auth/github/start

Initiates the GitHub OAuth flow. Redirects the browser to GitHub’s authorization page.

No request body. No auth required.


GET /api/auth/github/callback

OAuth callback. GitHub redirects here after the user authorizes. Sets a session cookie.

No request body. No auth required.


GET /api/auth/session

Returns the current session.

Response 200:

{ "user": { "id": "usr_01jx4n8rz", "github_login": "badri", "avatar_url": "https://avatars.githubusercontent.com/u/12345" }, "workspace_id": "ws_01jx4n9abc" }

Response 401: Not authenticated.


POST /api/auth/logout

Clears the session. No request body.

Response 200: { "ok": true }


Workspaces

GET /api/workspaces/me

Returns the current workspace.

Response 200:

{ "id": "ws_01jx4n9abc", "name": "badri", "github_org": "badri", "created_at": "2026-04-01T10:00:00Z", "plan": "hobby" }
curl https://gateway.vmkit.dev/api/workspaces/me \ -H "Authorization: Bearer vmk_your_key_here"

Repos

GET /api/repos

Lists all repos connected to the workspace.

Response 200:

[ { "id": "repo_01jx4n8abc", "full_name": "badri/my-fastapi-app", "language": "python", "framework": "fastapi", "last_scanned_at": "2026-05-20T14:30:00Z" } ]
curl https://gateway.vmkit.dev/api/repos \ -H "Authorization: Bearer vmk_your_key_here"

POST /api/repos

Connects a repo. The GitHub App must be installed on the target repository before calling this endpoint.

Request body:

{ "full_name": "badri/my-fastapi-app" }
FieldTypeRequiredDescription
full_namestringYesGitHub repo in owner/repo-name format.

Response 201:

{ "id": "repo_01jx4n8abc", "full_name": "badri/my-fastapi-app", "scan_status": "pending" }
curl -X POST https://gateway.vmkit.dev/api/repos \ -H "Authorization: Bearer vmk_your_key_here" \ -H "Content-Type: application/json" \ -d '{"full_name": "badri/my-fastapi-app"}'

GET /api/repos/{id}

Returns repo detail including the latest scan results.

Response 200:

{ "id": "repo_01jx4n8abc", "full_name": "badri/my-fastapi-app", "language": "python", "framework": "fastapi", "buildpack": "gcr.io/buildpacks/builder:v1", "procfile_detected": true, "dockerfile_detected": false, "scan_id": "sc_01jx4n8rz3q5", "last_scanned_at": "2026-05-20T14:30:00Z" }
curl https://gateway.vmkit.dev/api/repos/repo_01jx4n8abc \ -H "Authorization: Bearer vmk_your_key_here"

DELETE /api/repos/{id}

Disconnects a repo. Does not delete environments or destroy VMs.

Response 204: No content.

curl -X DELETE https://gateway.vmkit.dev/api/repos/repo_01jx4n8abc \ -H "Authorization: Bearer vmk_your_key_here"

POST /api/repos/{id}/scan

Triggers a re-scan of the repo. Asynchronous — poll GET /api/repos/{id} for updated results.

Response 202:

{ "scan_id": "sc_01jx5new", "status": "queued" }
curl -X POST https://gateway.vmkit.dev/api/repos/repo_01jx4n8abc/scan \ -H "Authorization: Bearer vmk_your_key_here"

Environments

GET /api/repos/{id}/environments

Lists environments for a repo.

Response 200:

[ { "id": "env_01jx4pa", "name": "staging", "status": "live", "app_url": "https://my-fastapi-app-staging.vmkit.app", "instance_id": "inst_01jx4n9abc", "created_at": "2026-05-01T09:00:00Z" } ]
curl https://gateway.vmkit.dev/api/repos/repo_01jx4n8abc/environments \ -H "Authorization: Bearer vmk_your_key_here"

POST /api/repos/{id}/environments

Creates a new environment for a repo.

Request body:

{ "name": "production", "compute_target_id": "ct_01jx4m7xyz", "region": "fsn1", "instance_type": "cax11" }
FieldTypeRequiredDescription
namestringYesEnvironment name (slug, lowercase, hyphens allowed).
compute_target_idstringYesID of the cloud provider to provision on.
regionstringNoProvider region code. Defaults to provider default.
instance_typestringNoVM size. Defaults to cax11 (Hetzner) or s-2vcpu-2gb (DigitalOcean).

Response 201:

{ "id": "env_01jx5pb", "name": "production", "status": "pending", "app_url": null, "instance_id": null }

GET /api/environments/{id}

Returns environment detail.

Response 200:

{ "id": "env_01jx4pa", "name": "staging", "status": "live", "app_url": "https://my-fastapi-app-staging.vmkit.app", "instance_id": "inst_01jx4n9abc", "repo_id": "repo_01jx4n8abc", "compute_target_id": "ct_01jx4m7xyz", "region": "fsn1", "instance_type": "cax11", "created_at": "2026-05-01T09:00:00Z", "last_deployed_at": "2026-05-20T15:00:00Z" }
curl https://gateway.vmkit.dev/api/environments/env_01jx4pa \ -H "Authorization: Bearer vmk_your_key_here"

PATCH /api/environments/{id}

Updates environment settings. All fields optional; only supplied fields are modified.

Request body:

{ "name": "staging-v2", "instance_type": "cx21" }
FieldTypeDescription
namestringRename the environment. Updates DNS subdomain on next deploy.
instance_typestringChange VM size. Takes effect on next VM provision.

Response 200: Full environment object (same shape as GET /api/environments/{id}).


Env Vars

GET /api/environments/{id}/env-vars

Lists env vars for an environment. Values for vars marked secret are redacted to "***".

Response 200:

[ { "key": "SENTRY_DSN", "value": "https://abc@o0.ingest.sentry.io/0", "secret": false }, { "key": "STRIPE_SECRET_KEY", "value": "***", "secret": true } ]
curl https://gateway.vmkit.dev/api/environments/env_01jx4pa/env-vars \ -H "Authorization: Bearer vmk_your_key_here"

PUT /api/environments/{id}/env-vars

Full replacement of all user-defined env vars for an environment. Vars not included in the request body are deleted.

Request body:

[ { "key": "SENTRY_DSN", "value": "https://abc@o0.ingest.sentry.io/0", "secret": false }, { "key": "STRIPE_SECRET_KEY", "value": "sk_live_xxx", "secret": true } ]
FieldTypeRequiredDescription
keystringYesEnv var name. Must match [A-Z_][A-Z0-9_]*.
valuestringYesString value. No quoting required.
secretbooleanNoIf true, value is stored encrypted and redacted in GET responses. Default: false.

Response 200: Array of saved env vars (secrets redacted).

curl -X PUT https://gateway.vmkit.dev/api/environments/env_01jx4pa/env-vars \ -H "Authorization: Bearer vmk_your_key_here" \ -H "Content-Type: application/json" \ -d '[{"key":"DATABASE_URL","value":"postgres://...","secret":true}]'

Env var changes take effect on the next deploy, not immediately. The running container is not restarted.


Compute Targets

GET /api/compute-targets

Lists all cloud providers registered in the workspace.

Response 200:

[ { "id": "ct_01jx4m7xyz", "provider": "hetzner", "name": "My Hetzner Account", "status": "valid", "default_region": "fsn1" } ]

POST /api/compute-targets

Registers a cloud provider.

Request body:

{ "provider": "hetzner", "name": "My Hetzner Account", "api_token": "HZNxxxxxxxx" }
FieldTypeRequiredDescription
provider"hetzner" | "digitalocean"YesCloud provider identifier.
namestringYesDisplay name for this credential set.
api_tokenstringYesProvider API token. Stored encrypted; never returned in responses.

Response 201:

{ "id": "ct_01jx4m7xyz", "provider": "hetzner", "name": "My Hetzner Account", "status": "valid", "default_region": "fsn1" }
curl -X POST https://gateway.vmkit.dev/api/compute-targets \ -H "Authorization: Bearer vmk_your_key_here" \ -H "Content-Type: application/json" \ -d '{"provider":"hetzner","name":"My Hetzner Account","api_token":"HZNxxx"}'

GET /api/compute-targets/{id}

Returns provider detail. The api_token field is never included in responses.

Response 200: Same shape as the POST response.


DELETE /api/compute-targets/{id}

Removes a cloud provider. Fails with 409 if any active environments reference this provider.

Response 204: No content.


Instances

GET /api/instances

Lists all VMs provisioned in the workspace.

Response 200:

[ { "id": "inst_01jx4n9abc", "provider": "hetzner", "region": "fsn1", "instance_type": "cax11", "ip": "65.21.100.200", "status": "running", "agent_connected": true, "environment_id": "env_01jx4pa" } ]

GET /api/instances/{id}

Returns instance detail including current health metrics.

Response 200:

{ "id": "inst_01jx4n9abc", "provider": "hetzner", "region": "fsn1", "instance_type": "cax11", "ip": "65.21.100.200", "status": "running", "agent_connected": true, "agent_version": "0.4.2", "environment_id": "env_01jx4pa", "metrics": { "cpu_pct": 8.2, "mem_pct": 41.5, "disk_pct": 22.1, "container_count": 2, "collected_at": "2026-05-22T10:00:00Z" } }
curl https://gateway.vmkit.dev/api/instances/inst_01jx4n9abc \ -H "Authorization: Bearer vmk_your_key_here"

DELETE /api/instances/{id}

Destroys the VM by calling the cloud provider’s delete API. Also removes the DNS A record. Irreversible.

Response 202: Destruction is asynchronous.

{ "status": "destroying" }

Deploys

GET /api/deploys

Lists deploys for the workspace, most recent first. Paginated.

Query parameters:

ParameterTypeDefaultDescription
pageinteger1Page number (1-indexed).
per_pageinteger20Results per page. Max 100.
environment_idstringFilter by environment.
repo_idstringFilter by repo.

Response 200:

{ "data": [ { "id": "dep_01jx5p0qr8", "repo_full_name": "badri/my-fastapi-app", "environment_name": "staging", "status": "succeeded", "sha": "abc1234", "triggered_by": "push", "created_at": "2026-05-22T09:00:00Z", "completed_at": "2026-05-22T09:07:32Z" } ], "total": 42, "page": 1, "per_page": 20 }

GET /api/deploys/{id}

Returns deploy detail.

Response 200:

{ "id": "dep_01jx5p0qr8", "repo_full_name": "badri/my-fastapi-app", "environment_name": "staging", "status": "succeeded", "sha": "abc1234", "triggered_by": "push", "run_url": "https://github.com/badri/my-fastapi-app/actions/runs/14912345", "app_url": "https://my-fastapi-app-staging.vmkit.app", "instance_id": "inst_01jx4n9abc", "created_at": "2026-05-22T09:00:00Z", "completed_at": "2026-05-22T09:07:32Z" }

POST /api/deploys/{id}/cancel

Cancels a deploy that is in running or queued status. Sends a cancellation signal to the GitHub Actions run.

Response 200: { "status": "cancelling" }

Response 409: Deploy is already in a terminal state.


GET /api/deploys/{id}/logs

Returns log lines for a deploy, combining GitHub Actions output and Kamal output.

Response 200:

{ "lines": [ { "ts": "2026-05-22T09:01:00Z", "source": "actions", "text": "Run buildpacks/github-init-tools@v1.2.3" }, { "ts": "2026-05-22T09:06:45Z", "source": "kamal", "text": "Finished all in 2.345 seconds" } ] }

Jobs

GET /api/jobs

Returns the background job queue status for the workspace.

Response 200:

{ "queued": 0, "running": 1, "jobs": [ { "id": "job_01jx5q1abc", "type": "vm_provision", "status": "running", "environment_id": "env_01jx4pa", "created_at": "2026-05-22T09:00:00Z" } ] }

API Keys

GET /api/api-keys

Lists API keys in the workspace. Secrets are never returned.

Response 200:

[ { "id": "ak_01jx4k9def", "name": "claude-code-local", "prefix": "vmk_a1b2", "created_at": "2026-05-01T08:00:00Z", "last_used_at": "2026-05-22T09:00:00Z" } ]

POST /api/api-keys

Creates an API key. The full token is returned exactly once in this response and is never retrievable again.

Request body:

{ "name": "claude-code-local" }

Response 201:

{ "id": "ak_01jx4k9def", "name": "claude-code-local", "token": "vmk_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" }
curl -X POST https://gateway.vmkit.dev/api/api-keys \ -H "Authorization: Bearer vmk_your_key_here" \ -H "Content-Type: application/json" \ -d '{"name": "claude-code-local"}'

DELETE /api/api-keys/{id}

Revokes an API key immediately. Any in-flight requests using the key will fail with 401 after revocation.

Response 204: No content.


Webhooks

POST /api/webhooks/github

GitHub App webhook receiver. Validates the X-Hub-Signature-256 header using the shared webhook secret configured in the GitHub App settings.

Not for direct use. This endpoint is called by GitHub, not by API consumers.

HeaderRequiredDescription
X-Hub-Signature-256YesHMAC-SHA256 of the raw request body, prefixed sha256=.
X-GitHub-EventYesEvent type (e.g. workflow_run, push, installation).
X-GitHub-DeliveryYesUnique delivery UUID from GitHub.

Response 200: { "ok": true } on successful receipt and validation.

Response 401: Signature mismatch.


Error Shapes

All error responses use a consistent JSON envelope:

{ "error": "error_code_snake_case", "message": "Human-readable description." }
HTTP statusCommon error values
400validation_error, invalid_token, invalid_provider
401unauthorized, token_expired, token_revoked
403forbidden, github_app_not_installed
404repo_not_found, environment_not_found, instance_not_found, deploy_not_found
409compute_target_in_use, deploy_already_terminal
500internal_error, provider_api_error
Last updated on