Getting Started
The Staxa API lets you programmatically create and manage isolated tenant environments. Every action available in the dashboard is also available through the API.
Base URL: https://api.staxa.dev/api/v1
Quick Example — Create a tenant:
curl -X POST https://api.staxa.dev/api/v1/tenants \
-H "Authorization: Bearer sk_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "acme-corp",
"source_type": "github",
"repo_url": "https://github.com/acme/webapp",
"branch": "main"
}'
This creates a tenant named "acme-corp", clones the repo, auto-detects the framework, builds and deploys it, and serves it at https://acme-corp.tenants.staxa.dev — all from a single API call. The response includes an events_url you can subscribe to for real-time deployment progress via SSE.
Authentication
The Staxa API accepts two authentication modes:
API Key (Programmatic)
For server-to-server integrations and automation. Create API keys from the dashboard at Settings → API Keys, or via POST /api/v1/api-keys.
curl https://api.staxa.dev/api/v1/tenants \
-H "Authorization: Bearer sk_live_a1b2c3d4e5f6..."
API keys come in two modes:
- live —
sk_live_...— full access, use in production - test —
sk_test_...— limited access, use in development
Clerk JWT (Dashboard)
Used automatically by the dashboard. Most developers won't need this — use API keys for programmatic access.
curl https://api.staxa.dev/api/v1/tenants \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."
Response Format
All API responses follow one of three shapes:
Single resource:
{
"data": { ... },
"meta": { "request_id": "..." }
}
List (paginated):
{
"data": [ ... ],
"meta": { "total": 42, "limit": 20, "offset": 0 }
}
Error:
{
"error": {
"code": "...",
"message": "...",
"details": { ... }
}
}
Error Handling
The API uses standard HTTP status codes:
| Code | Meaning |
|---|---|
| 200 | Success (GET, PATCH) |
| 201 | Created (synchronous POST) |
| 202 | Accepted (async operations — tenant creation, deploys) |
| 204 | No Content (DELETE) |
| 400 | Bad Request (validation) |
| 401 | Unauthorized |
| 403 | Forbidden (e.g., tenant limit reached) |
| 404 | Not Found |
| 409 | Conflict (subdomain taken) |
| 422 | Unprocessable Entity |
| 429 | Rate Limited |
| 500 | Internal Server Error |
Rate Limits
| Endpoint | Limit | Window |
|---|---|---|
POST /api/v1/tenants | 10 | per minute |
POST /tenants/:id/deploy | 20 | per minute |
GET /api/v1/tenants | 60 | per minute |
GET /api/v1/tenants/:id | 120 | per minute |
GET /tenants/:id/events | 10 | per minute (SSE connections) |
POST /api/v1/api-keys | 5 | per minute |
| All other endpoints | 60 | per minute |
Rate limit headers are included in every response:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1708790460
Webhooks (Coming Soon)
Webhooks will allow you to receive real-time notifications when events occur in your tenant environments.
Planned event format:
{
"event": "tenant.ready",
"tenant_id": "ten_a1b2c3d4e5f6",
"data": {
"status": "ready",
"url": "https://acme.tenants.staxa.dev"
},
"timestamp": "2025-02-24T14:31:00Z"
}
Planned events: tenant.ready, tenant.failed, tenant.deleted, deployment.started, deployment.succeeded, deployment.failed.
Coming soon — contact us if you need webhook integration today.
Account
Provider registration, profile, and account management.
Register
Register a new provider account. Idempotent — calling twice returns the existing provider.
Parameters
- Name
email- Type
- string
- Description
Required.Provider email address
- Name
name- Type
- string
- Description
Provider display name
POST /api/v1/register
curl -X POST https://api.staxa.dev/api/v1/register \
-H "Authorization: Bearer <clerk-jwt>" \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com", "name": "Acme Inc"}'Get Account
Returns the authenticated provider profile with tenant count.
GET /api/v1/account
{
"data": {
"id": "prv_a1b2c3d4",
"email": "user@example.com",
"name": "Acme Inc",
"tenant_count": 3,
"created_at": "2026-01-15T10:30:00Z"
}
}Update Account
Update the authenticated provider profile.
Parameters
- Name
name- Type
- string
- Description
New display name
PATCH /api/v1/account
{
"name": "Acme Corporation"
}API Keys
Create and manage API keys for programmatic access.
List API Keys
List all API keys for the authenticated provider. Returns key prefix only, never the full key.
GET /api/v1/api-keys
{
"data": [
{
"id": "key_a1b2c3d4",
"name": "Production",
"prefix": "sk_live_a1b2",
"mode": "live",
"scopes": ["*"],
"expires_at": null,
"created_at": "2026-01-15T10:30:00Z"
}
]
}Create API Key
Create a new API key. The raw key is returned ONCE in the response — store it securely.
Parameters
- Name
name- Type
- string
- Description
Required.Human-readable key name
- Name
scopes- Type
- string[]
- Description
Permission scopes Defaults to
["*"].
- Name
mode- Type
- string
- Description
Key mode: "live" or "test" Defaults to
live.
- Name
expires_at- Type
- string
- Description
ISO 8601 expiration date
Error Codes
| Code | Condition |
|---|---|
| 400 | Missing required field "name" |
POST /api/v1/api-keys
curl -X POST https://api.staxa.dev/api/v1/api-keys \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{"name": "Production", "mode": "live"}'Delete API Key
Revoke and delete an API key. Takes effect immediately.
Error Codes
| Code | Condition |
|---|---|
| 404 | API key not found |
DELETE /api/v1/api-keys/{id}
No response body (204 No Content).Tenants
Create, list, update, and delete tenant environments.
List Tenants
List all tenants for the authenticated provider with pagination and filtering.
Parameters
- Name
status- Type
- string
- Description
Filter by status (e.g., ready, deploying, failed)
- Name
limit- Type
- int
- Description
Results per page Defaults to
20.
- Name
offset- Type
- int
- Description
Pagination offset Defaults to
0.
- Name
sort- Type
- string
- Description
Sort field (e.g., created_at, name)
- Name
search- Type
- string
- Description
Search by tenant name
GET /api/v1/tenants
{
"data": [
{
"id": "ten_a1b2c3d4",
"name": "acme-corp",
"display_name": "Acme Corp",
"status": "ready",
"url": "https://acme-corp.tenants.staxa.dev",
"created_at": "2026-01-15T10:30:00Z"
}
],
"meta": { "total": 1, "limit": 20, "offset": 0 }
}Create Tenant
Create a new tenant environment. Supports single-service (legacy) or multi-service request body.
Parameters
- Name
name- Type
- string
- Description
Required.Unique tenant name (used as subdomain)
- Name
display_name- Type
- string
- Description
Human-readable display name
- Name
source_type- Type
- string
- Description
Source type: "github" or "image" (single-service mode)
- Name
repo_url- Type
- string
- Description
Git repository URL (single-service mode)
- Name
branch- Type
- string
- Description
Git branch Defaults to
main.
- Name
services- Type
- object[]
- Description
Array of service definitions (multi-service mode)
- Name
env- Type
- object
- Description
Environment variables as key-value pairs
Error Codes
| Code | Condition |
|---|---|
| 400 | Validation error (missing name, invalid source_type, duplicate service names) |
| 409 | Tenant name already taken |
| 422 | Tenant limit reached for this provider |
POST /api/v1/tenants
curl -X POST https://api.staxa.dev/api/v1/tenants \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"name": "acme-corp",
"source_type": "github",
"repo_url": "https://github.com/acme/webapp",
"branch": "main"
}'Get Tenant
Get full details for a specific tenant including services, domains, and recent deployments.
Error Codes
| Code | Condition |
|---|---|
| 404 | Tenant not found |
GET /api/v1/tenants/{id}
{
"data": {
"id": "ten_a1b2c3d4",
"name": "acme-corp",
"display_name": "Acme Corp",
"status": "ready",
"url": "https://acme-corp.tenants.staxa.dev",
"services": [],
"domains": [],
"created_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-01-15T10:35:00Z"
}
}Update Tenant
Partial update of a tenant. Only provided fields are changed.
Parameters
- Name
display_name- Type
- string
- Description
Human-readable name
- Name
branch- Type
- string
- Description
Default git branch
- Name
resource_size- Type
- string
- Description
Resource tier: small, medium, large
- Name
app_port- Type
- int
- Description
Application port
- Name
egress_mode- Type
- string
- Description
Network egress mode: open or restricted
Error Codes
| Code | Condition |
|---|---|
| 404 | Tenant not found |
PATCH /api/v1/tenants/{id}
{
"display_name": "Acme Corp Updated",
"resource_size": "medium"
}Delete Tenant
Delete a tenant and all its resources. This is an async operation.
Error Codes
| Code | Condition |
|---|---|
| 404 | Tenant not found |
DELETE /api/v1/tenants/{id}
No response body (202 Accepted). Deletion happens asynchronously.Services
Manage individual services within a multi-service tenant.
List Services
List all services for a tenant.
GET /api/v1/tenants/{id}/services
{
"data": [
{
"name": "web",
"source_type": "github",
"repo_url": "https://github.com/acme/webapp",
"branch": "main",
"status": "running",
"is_primary": true,
"is_routable": true,
"port": 3000
}
]
}Add Service
Add a new service to an existing tenant. Triggers an automatic deployment.
Parameters
- Name
name- Type
- string
- Description
Required.Unique service name within the tenant
- Name
source_type- Type
- string
- Description
Required.Source type: "github" or "image"
- Name
repo_url- Type
- string
- Description
Git repository URL
- Name
branch- Type
- string
- Description
Git branch Defaults to
main.
- Name
image_url- Type
- string
- Description
Container image URL (for source_type "image")
- Name
port- Type
- int
- Description
Service port
- Name
is_routable- Type
- boolean
- Description
Whether the service receives HTTP traffic Defaults to
false.
- Name
resource_size- Type
- string
- Description
Resource tier: small, medium, large Defaults to
small.
Error Codes
| Code | Condition |
|---|---|
| 400 | Missing required field (name or source_type) |
| 409 | Service name already exists in this tenant |
| 422 | Tenant is not in a deployable state |
POST /api/v1/tenants/{id}/services
{
"name": "api",
"source_type": "github",
"repo_url": "https://github.com/acme/backend",
"branch": "main",
"port": 8080,
"is_routable": true,
"resource_size": "small"
}Remove Service
Remove a service from a tenant. Cannot remove the primary service.
Error Codes
| Code | Condition |
|---|---|
| 400 | Cannot remove the primary service |
| 404 | Service not found |
DELETE /api/v1/tenants/{id}/services/{serviceName}
No response body (204 No Content).Deploy Service
Trigger a deployment for a specific service.
POST /api/v1/tenants/{id}/services/{serviceName}/deploy
{
"data": {
"id": "dep_x1y2z3",
"service_name": "web",
"status": "building",
"created_at": "2026-01-15T10:30:00Z"
}
}List Service Deployments
List deployment history for a specific service.
GET /api/v1/tenants/{id}/services/{serviceName}/deployments
{
"data": [
{
"id": "dep_x1y2z3",
"service_name": "web",
"status": "succeeded",
"commit_sha": "abc123",
"created_at": "2026-01-15T10:30:00Z",
"finished_at": "2026-01-15T10:32:00Z"
}
]
}Service Logs
Get logs for a specific service.
Parameters
- Name
lines- Type
- int
- Description
Number of log lines to return Defaults to
100.
- Name
follow- Type
- boolean
- Description
Stream logs in real-time (SSE) Defaults to
false.
GET /api/v1/tenants/{id}/services/{serviceName}/logs
Raw text log output (Content-Type: text/plain).Deploy All Services
Deploy all services in a tenant at once.
POST /api/v1/tenants/{id}/deploy
{
"data": {
"tenant_id": "ten_a1b2c3d4",
"status": "deploying",
"services": ["web", "api"]
}
}Deployments
Trigger, list, inspect, and rollback deployments.
List Deployments
List all deployments for a tenant.
GET /api/v1/tenants/{id}/deployments
{
"data": [
{
"id": "dep_x1y2z3",
"status": "succeeded",
"branch": "main",
"commit_sha": "abc123def456",
"created_at": "2026-01-15T10:30:00Z",
"finished_at": "2026-01-15T10:32:00Z"
}
],
"meta": { "total": 5 }
}Create Deployment
Trigger a new deployment for a tenant.
Parameters
- Name
branch- Type
- string
- Description
Git branch to deploy
- Name
commit_sha- Type
- string
- Description
Specific commit to deploy
POST /api/v1/tenants/{id}/deployments
{
"branch": "main",
"commit_sha": "abc123def456"
}Get Deployment
Get details for a specific deployment.
GET /api/v1/tenants/{id}/deployments/{depId}
{
"data": {
"id": "dep_x1y2z3",
"status": "succeeded",
"branch": "main",
"commit_sha": "abc123def456",
"build_log_url": "/api/v1/tenants/ten_a1b2c3d4/deployments/dep_x1y2z3/logs",
"created_at": "2026-01-15T10:30:00Z",
"finished_at": "2026-01-15T10:32:00Z"
}
}Rollback Deployment
Rollback to a specific previous deployment.
POST /api/v1/tenants/{id}/deployments/{depId}/rollback
{
"data": {
"id": "dep_new123",
"status": "building",
"rollback_from": "dep_x1y2z3",
"created_at": "2026-01-15T11:00:00Z"
}
}Environment Variables
Manage environment variables for tenant workloads.
Get Env Vars
List environment variables for a tenant. Secret values are masked.
GET /api/v1/tenants/{id}/env
{
"data": [
{ "key": "NODE_ENV", "value": "production" },
{ "key": "DATABASE_URL", "value": "****" }
]
}Bulk Set Env Vars
Set environment variables in bulk. Replaces all existing variables.
PUT /api/v1/tenants/{id}/env
{
"NODE_ENV": "production",
"DATABASE_URL": "postgres://...",
"API_KEY": "secret123"
}Domains
Custom domain management with DNS verification.
List Domains
List custom domains for a tenant. Includes server IP for DNS configuration.
GET /api/v1/tenants/{id}/domains
{
"data": {
"domains": [
{
"id": "dom_a1b2c3",
"domain": "app.acme.com",
"is_primary": true,
"verified": true,
"ssl_status": "active",
"created_at": "2026-01-15T10:30:00Z"
}
],
"server_ip": "65.108.x.x"
}
}Add Domain
Add a custom domain to a tenant.
Parameters
- Name
domain- Type
- string
- Description
Required.Custom domain name
- Name
is_primary- Type
- boolean
- Description
Set as primary domain Defaults to
false.
- Name
service_id- Type
- string
- Description
Route to a specific service (multi-service tenants)
POST /api/v1/tenants/{id}/domains
{
"domain": "app.acme.com",
"is_primary": true,
"service_id": "web"
}Verify Domain
Trigger DNS verification for a custom domain.
POST /api/v1/tenants/{id}/domains/{domId}/verify
{
"data": {
"id": "dom_a1b2c3",
"domain": "app.acme.com",
"verified": true,
"ssl_status": "provisioning"
}
}Delete Domain
Remove a custom domain from a tenant.
DELETE /api/v1/tenants/{id}/domains/{domId}
No response body (204 No Content).Network Rules
Inbound and outbound network access control.
List Network Rules
List network rules for a tenant with optional filtering.
Parameters
- Name
direction- Type
- string
- Description
Filter: "inbound" or "outbound"
- Name
status- Type
- string
- Description
Filter by status Defaults to
active.
GET /api/v1/tenants/{id}/network-rules
{
"data": [
{
"id": "nr_a1b2c3",
"direction": "inbound",
"protocol": "tcp",
"target_port": 5432,
"allocated_port": 30100,
"status": "active",
"connection_string": "65.108.x.x:30100"
}
]
}Create Network Rule
Create a network rule. Inbound rules allocate a NodePort; outbound rules require restricted egress mode.
Parameters
- Name
direction- Type
- string
- Description
Required."inbound" or "outbound"
- Name
protocol- Type
- string
- Description
Required.Protocol: "tcp" or "udp"
- Name
target_service- Type
- string
- Description
Target service name (inbound)
- Name
target_port- Type
- int
- Description
Target port (inbound)
- Name
source_cidrs- Type
- string[]
- Description
Allowed source IPs (inbound)
- Name
destination_cidrs- Type
- string[]
- Description
Allowed destination IPs (outbound)
- Name
destination_port- Type
- int
- Description
Destination port (outbound)
- Name
ttl_hours- Type
- int
- Description
Auto-expire after N hours
- Name
description- Type
- string
- Description
Human-readable description
Error Codes
| Code | Condition |
|---|---|
| 400 | Invalid CIDR format, CIDR too wide, or too many CIDRs |
| 409 | NodePort pool exhausted (inbound) |
| 422 | Rule limit reached, or egress_mode must be "restricted" for outbound rules |
POST /api/v1/tenants/{id}/network-rules
**Inbound rule:**
```json
{
"direction": "inbound",
"protocol": "tcp",
"target_service": "db",
"target_port": 5432,
"source_cidrs": ["203.0.113.0/24"],
"description": "Database access from office"
}
```
**Outbound rule:**
```json
{
"direction": "outbound",
"protocol": "tcp",
"destination_cidrs": ["10.0.0.0/8"],
"destination_port": 443,
"description": "Allow HTTPS to internal network"
}
```Get Network Rule
Get details for a specific network rule.
GET /api/v1/tenants/{id}/network-rules/{ruleId}
{
"data": {
"id": "nr_a1b2c3",
"direction": "inbound",
"protocol": "tcp",
"target_port": 5432,
"allocated_port": 30100,
"status": "active",
"connection_string": "65.108.x.x:30100",
"source_cidrs": ["203.0.113.0/24"]
}
}Delete Network Rule
Delete a network rule. Inbound rules release the allocated NodePort.
DELETE /api/v1/tenants/{id}/network-rules/{ruleId}
No response body (204 No Content).Real-Time
SSE event streams and live container logs.
Events (SSE)
Subscribe to real-time deployment events via Server-Sent Events (SSE).
GET /api/v1/tenants/{id}/events
curl -N https://api.staxa.dev/api/v1/tenants/ten_a1b2c3d4/events \
-H "Authorization: Bearer sk_live_..." \
-H "Accept: text/event-stream"Container Logs
Get container logs for a tenant.
Parameters
- Name
lines- Type
- int
- Description
Number of log lines to return Defaults to
200.
- Name
follow- Type
- boolean
- Description
Stream logs in real-time Defaults to
false.
GET /api/v1/tenants/{id}/logs
curl https://api.staxa.dev/api/v1/tenants/ten_a1b2c3d4/logs?lines=50 \
-H "Authorization: Bearer sk_live_..."Templates
Pre-built application templates.
List Templates
List available application templates for quick-start tenant creation.
GET /api/v1/templates
{
"data": [
{
"id": "tpl_nextjs",
"name": "Next.js Starter",
"framework": "nextjs",
"description": "Production-ready Next.js application",
"repo_url": "https://github.com/staxa/template-nextjs"
}
]
}Get Template
Get details for a specific template.
GET /api/v1/templates/{id}
{
"data": {
"id": "tpl_nextjs",
"name": "Next.js Starter",
"framework": "nextjs",
"description": "Production-ready Next.js application",
"repo_url": "https://github.com/staxa/template-nextjs",
"default_env": { "NODE_ENV": "production" },
"default_port": 3000
}
}GitHub App
GitHub App installation and repository access.
Get GitHub Installation
Get the current GitHub App installation status for the authenticated provider.
GET /api/v1/github/installation
{
"data": {
"installation_id": 12345678,
"account": "acme-org",
"status": "active",
"created_at": "2026-01-15T10:30:00Z"
}
}Register GitHub Installation
Register a GitHub App installation for the provider. Called after the GitHub OAuth flow.
Parameters
- Name
installation_id- Type
- int
- Description
Required.GitHub App installation ID from the OAuth callback
POST /api/v1/github/installation
{
"installation_id": 12345678
}Delete GitHub Installation
Remove the GitHub App installation link from the provider account.
DELETE /api/v1/github/installation
No response body (204 No Content).List GitHub Repos
List repositories accessible via the GitHub App installation.
GET /api/v1/github/repos
{
"data": [
{
"full_name": "acme-org/webapp",
"private": true,
"default_branch": "main",
"language": "TypeScript"
}
]
}List GitHub Branches
List branches for a specific repository.
GET /api/v1/github/repos/{owner}/{repo}/branches
{
"data": [
{ "name": "main", "protected": true },
{ "name": "develop", "protected": false },
{ "name": "feature/auth", "protected": false }
]
}Registry Credentials
Private container registry authentication.
List Registries
List saved container registry credentials.
GET /api/v1/registries
{
"data": [
{
"id": "reg_a1b2c3",
"name": "Docker Hub",
"registry_url": "https://index.docker.io/v1/",
"username": "acme",
"verified": true,
"created_at": "2026-01-15T10:30:00Z"
}
]
}Create Registry Credential
Save credentials for a private container registry.
POST /api/v1/registries
{
"name": "Docker Hub",
"registry_url": "https://index.docker.io/v1/",
"username": "acme",
"password": "dckr_pat_..."
}Get Registry Credential
Get details for a saved registry credential. Password is never returned.
GET /api/v1/registries/{credId}
{
"data": {
"id": "reg_a1b2c3",
"name": "Docker Hub",
"registry_url": "https://index.docker.io/v1/",
"username": "acme",
"verified": true
}
}Update Registry Credential
Update a saved registry credential.
PATCH /api/v1/registries/{credId}
{
"password": "dckr_pat_new..."
}Delete Registry Credential
Delete a saved registry credential.
DELETE /api/v1/registries/{credId}
No response body (204 No Content).Verify Registry Credential
Test that the saved credentials can authenticate with the registry.
POST /api/v1/registries/{credId}/verify
{
"data": {
"status": "verified"
}
}List Registry Repos
List repositories available in the registry.
Parameters
- Name
q- Type
- string
- Description
Search query to filter repos
- Name
limit- Type
- int
- Description
Maximum results to return Defaults to
25.
GET /api/v1/registries/{credId}/repos
{
"data": [
{ "name": "acme/webapp", "tag_count": 12 },
{ "name": "acme/api", "tag_count": 8 }
]
}List Registry Tags
List tags for a specific repository in the registry.
GET /api/v1/registries/{credId}/repos/{repo}/tags
{
"data": [
{ "name": "latest", "digest": "sha256:abc123...", "size": 52428800 },
{ "name": "v1.2.3", "digest": "sha256:def456...", "size": 52428800 }
]
}