Getting Started
The Staxa API creates and manages isolated tenant environments. Everything you need to deploy and run apps on Staxa is here. Account and billing setup lives in the dashboard.
Base URL: https://api.staxa.dev/api/v1
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"
}'
That single call clones the repo, detects the framework, builds it, and serves the result at https://acme-corp.tenants.staxa.dev. Creation returns 202 straight away and the first deploy starts on its own; subscribe to GET /api/v1/tenants/{id}/events over SSE to watch it progress.
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..."
A key is sk_live_ or sk_test_ followed by 32 hex characters. Both modes carry the same access — the mode is a label, so you can tell a production key from a development one at a glance. Only a bcrypt hash and the first 16 characters are stored, so the raw key exists nowhere but the create response.
Clerk JWT (Dashboard)
The dashboard uses this automatically. For anything you write yourself, use an API key.
curl https://api.staxa.dev/api/v1/tenants \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."
Response Format
Every response takes one of three shapes.
Single resource:
{
"data": { ... },
"meta": { "request_id": "..." }
}
List (paginated):
{
"data": [ ... ],
"pagination": { "total": 42, "limit": 20, "offset": 0 },
"meta": { "request_id": "..." }
}
Error:
{
"error": {
"code": "...",
"message": "...",
"details": { ... }
}
}
Paginated endpoints accept limit (1–100, default 20) and offset (default 0) as query parameters. Not every list is paginated — shorter collections such as services, domains, env vars, and network rules come back as a plain array under data, with no pagination block.
Error Handling
The API uses standard HTTP status codes:
| Code | Meaning |
|---|---|
| 200 | Success (GET, PATCH, PUT) |
| 201 | Created (synchronous POST) |
| 202 | Accepted (async operations: tenant creation, deploys, deletes) |
| 204 | No Content (DELETE) |
| 400 | Bad Request (validation) |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not Found |
| 409 | Conflict (name already taken, port pool exhausted) |
| 422 | Unprocessable Entity (tenant limit reached, tenant busy) |
| 429 | Rate Limited |
| 500 | Internal Server Error |
| 501 | Not Implemented (feature not configured on this deployment) |
The error.code field carries a machine-readable string to branch on: BAD_REQUEST, UNAUTHORIZED, FORBIDDEN, NOT_FOUND, CONFLICT, UNPROCESSABLE_ENTITY, TOO_MANY_REQUESTS, INTERNAL_SERVER_ERROR, NOT_IMPLEMENTED, SERVICE_UNAVAILABLE.
Network rules and registry credentials add codes of their own: INVALID_CIDR, CIDR_TOO_WIDE, TOO_MANY_CIDRS, RULE_LIMIT_REACHED, EGRESS_MODE_REQUIRED, PORT_POOL_EXHAUSTED, REGISTRY_VERIFICATION_FAILED, REGISTRY_LIMIT_REACHED.
Rate Limits
Authenticated requests share a single limit: 100 requests per minute per provider, counted across every endpoint. The window is fixed and resets at the top of each minute.
The template catalog is public and needs no authentication, so it is limited by IP instead: 60 requests per minute.
Every response carries the rate limit headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1708790460
Going over returns 429 with code TOO_MANY_REQUESTS. If the rate limiter itself is unreachable, requests are allowed through rather than rejected.
Webhooks (Coming Soon)
Webhooks will post notifications to a URL you control 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.
Not available yet. Get in touch if you need webhook integration before then.
API Keys
Create and manage API keys for programmatic access.
List API Keys
List all API keys for the authenticated provider. Only the key prefix comes back, never the full key.
GET /api/v1/api-keys
{
"data": [
{
"id": "key_a1b2c3d4",
"provider_id": "prov_a1b2c3d4",
"name": "Production",
"prefix": "sk_live_a1b2c3d4",
"scopes": ["*"],
"mode": "live",
"last_used": "2026-01-16T08:12:00Z",
"expires_at": null,
"status": "active",
"created_at": "2026-01-15T10:30:00Z"
}
]
}Create API Key
Create a new API key. The response contains the raw key, and it is the only time you will see it.
Parameters
- Name
name- Type
- string
- Description
Required.A name for the key, for your own reference
- 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 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. Paginated, with an optional status filter.
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",
"provider_id": "prov_a1b2c3d4",
"name": "acme-corp",
"display_name": "Acme Corp",
"status": "ready",
"source_type": "github",
"repo_url": "https://github.com/acme/webapp",
"branch": "main",
"app_port": 3000,
"resource_size": "small",
"egress_mode": "allow_all",
"deployment_url": "https://acme-corp.tenants.staxa.dev",
"created_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-01-15T10:35:00Z"
}
],
"pagination": { "total": 1, "limit": 20, "offset": 0 }
}Create Tenant
Create a new tenant environment. Supports a 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
Display name for the tenant
- 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 tenant, including its services.
Error Codes
| Code | Condition |
|---|---|
| 404 | Tenant not found |
GET /api/v1/tenants/{id}
{
"data": {
"id": "ten_a1b2c3d4",
"provider_id": "prov_a1b2c3d4",
"name": "acme-corp",
"display_name": "Acme Corp",
"status": "ready",
"current_stage": "ready",
"source_type": "github",
"repo_url": "https://github.com/acme/webapp",
"branch": "main",
"app_port": 3000,
"resource_size": "small",
"cpu_request": "25m",
"cpu_limit": "500m",
"memory_request": "64Mi",
"memory_limit": "512Mi",
"storage_limit": "5Gi",
"current_image": "registry.staxa.dev/acme-corp:v3",
"deployment_url": "https://acme-corp.tenants.staxa.dev",
"egress_mode": "allow_all",
"health_check_type": "http",
"health_check_path": "/",
"k8s_namespace": "ten-acme-corp",
"services": [],
"metadata": {},
"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
Display name for the tenant
- 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",
"repo_url": "https://github.com/acme/webapp",
"branch": "release",
"dockerfile_path": "docker/Dockerfile",
"build_context": ".",
"resource_size": "medium",
"app_port": 3000,
"egress_mode": "restricted",
"health_check_type": "http",
"health_check_path": "/healthz",
"health_check_port": 3000
}Delete Tenant
Delete a tenant and every resource it owns.
Error Codes
| Code | Condition |
|---|---|
| 404 | Tenant not found |
DELETE /api/v1/tenants/{id}
{
"data": {
"status": "accepted",
"message": "tenant deletion queued"
}
}Services
Manage individual services inside a multi-service tenant.
List Services
List all services for a tenant.
GET /api/v1/tenants/{id}/services
{
"data": [
{
"id": "svc_9f8e7d6c",
"tenant_id": "ten_a1b2c3d4",
"name": "web",
"display_name": "Web",
"source_type": "github",
"repo_url": "https://github.com/acme/webapp",
"branch": "main",
"dockerfile_path": "Dockerfile",
"build_context": ".",
"framework": "nextjs",
"port": 3000,
"is_routable": true,
"is_primary": true,
"status": "running",
"image_tag": "v3",
"deploy_priority": 0,
"resource_size": "small",
"health_check_type": "http",
"health_check_path": "/",
"created_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-01-15T10:35:00Z"
}
]
}Add Service
Add a new service to an existing tenant.
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",
"display_name": "API",
"source_type": "github",
"repo_url": "https://github.com/acme/backend",
"branch": "main",
"dockerfile_path": "Dockerfile",
"build_context": ".",
"port": 8080,
"is_routable": true,
"deploy_priority": 1,
"resource_size": "small",
"health_check_type": "http",
"health_check_path": "/healthz"
}Remove Service
Remove a service from a tenant. The primary service cannot be removed.
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 one service.
POST /api/v1/tenants/{id}/services/{serviceName}/deploy
{
"data": {
"id": "dep_x1y2z3",
"tenant_id": "ten_a1b2c3d4",
"service_id": "svc_9f8e7d6c",
"provider_id": "prov_a1b2c3d4",
"image_tag": "pending",
"source_type": "github",
"deploy_type": "full",
"trigger": "api",
"status": "pending",
"version": 4,
"is_current": false,
"metadata": {},
"created_at": "2026-01-15T10:30:00Z"
}
}List Service Deployments
List deployment history for one service.
GET /api/v1/tenants/{id}/services/{serviceName}/deployments
{
"data": [
{
"id": "dep_x1y2z3",
"tenant_id": "ten_a1b2c3d4",
"service_id": "svc_9f8e7d6c",
"image_tag": "registry.staxa.dev/acme-corp-web:v3",
"source_ref": "abc123def456",
"source_type": "github",
"deploy_type": "full",
"trigger": "api",
"status": "succeeded",
"started_at": "2026-01-15T10:30:05Z",
"completed_at": "2026-01-15T10:32:00Z",
"duration_ms": 115000,
"version": 3,
"is_current": true,
"has_config_snapshot": true,
"has_retained_secrets": true,
"created_at": "2026-01-15T10:30:00Z"
}
],
"pagination": { "total": 3, "limit": 20, "offset": 0 }
}Service Logs
Stream logs for one 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
curl "https://api.staxa.dev/api/v1/tenants/ten_a1b2c3d4/services/api/logs?lines=200&follow=true" \
-H "Authorization: Bearer sk_live_..."Deploy All Services
Deploy every service in a tenant at once.
POST /api/v1/tenants/{id}/deploy
{
"data": {
"id": "dep_x1y2z3",
"tenant_id": "ten_a1b2c3d4",
"provider_id": "prov_a1b2c3d4",
"image_tag": "pending",
"source_type": "github",
"deploy_type": "full",
"trigger": "api",
"status": "pending",
"version": 5,
"is_current": false,
"metadata": {},
"created_at": "2026-01-15T10:30:00Z"
}
}Rollback Service Deployment
Roll one service back to an earlier deployment of that same service.
POST /api/v1/tenants/{id}/services/{serviceName}/deployments/{depId}/rollback
curl -X POST \
https://api.staxa.dev/api/v1/tenants/ten_a1b2c3d4/services/web/deployments/dep_x1y2z3/rollback \
-H "Authorization: Bearer $STAXA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"refresh_config": false}'Deployments
Trigger, inspect, and roll back deployments.
List Deployments
List all deployments for a tenant, newest first.
GET /api/v1/tenants/{id}/deployments
{
"data": [
{
"id": "dep_x1y2z3",
"tenant_id": "ten_a1b2c3d4",
"provider_id": "prov_a1b2c3d4",
"image_tag": "registry.staxa.dev/acme-corp:v3",
"source_ref": "abc123def456",
"source_type": "github",
"deploy_type": "full",
"trigger": "api",
"status": "succeeded",
"started_at": "2026-01-15T10:30:05Z",
"completed_at": "2026-01-15T10:32:00Z",
"duration_ms": 115000,
"version": 3,
"is_current": true,
"has_config_snapshot": true,
"has_retained_secrets": true,
"metadata": {},
"created_at": "2026-01-15T10:30:00Z"
}
],
"pagination": { "total": 5, "limit": 20, "offset": 0 }
}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
{
"image_tag": "registry.staxa.dev/acme-corp:v4",
"source_ref": "abc123def456",
"source_type": "github",
"deploy_type": "full",
"trigger": "api"
}Get Deployment
Get details for one deployment.
GET /api/v1/tenants/{id}/deployments/{depId}
{
"data": {
"id": "dep_x1y2z3",
"tenant_id": "ten_a1b2c3d4",
"provider_id": "prov_a1b2c3d4",
"image_tag": "registry.staxa.dev/acme-corp:v3",
"source_ref": "abc123def456",
"source_type": "github",
"deploy_type": "full",
"trigger": "api",
"status": "failed",
"started_at": "2026-01-15T10:30:05Z",
"completed_at": "2026-01-15T10:31:10Z",
"duration_ms": 65000,
"error_stage": "build",
"error_message": "build failed: exit status 1",
"build_logs": "...",
"version": 3,
"is_current": false,
"has_config_snapshot": true,
"has_retained_secrets": true,
"metadata": {},
"created_at": "2026-01-15T10:30:00Z"
}
}Rollback Deployment
Roll back to a specific earlier deployment, restoring its image and the configuration it ran with.
POST /api/v1/tenants/{id}/deployments/{depId}/rollback
{
"refresh_config": false
}Environment Variables
Manage environment variables for tenant workloads.
Get Env Vars
List environment variables for a tenant.
GET /api/v1/tenants/{id}/env
{
"data": [
{
"id": "6f1c9a2e-8b34-4d51-9f77-2a0c5e8b1d43",
"key": "NODE_ENV",
"value": "production",
"is_secret": false,
"source": "user"
},
{
"id": "b28d7f04-15ce-4a9b-8e60-7c3f9a1d6e52",
"key": "DATABASE_URL",
"value": "[redacted]",
"is_secret": true,
"source": "system"
}
]
}Bulk Set Env Vars
Create or update environment variables in bulk.
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.
GET /api/v1/tenants/{id}/domains
{
"data": {
"domains": [
{
"id": "dom_a1b2c3",
"tenant_id": "ten_a1b2c3d4",
"domain": "app.acme.com",
"type": "custom",
"status": "verified",
"verification_record": "staxa-verify=ver_a1b2c3d4",
"verified_at": "2026-01-15T11:00:00Z",
"ssl_status": "active",
"is_primary": true,
"service_id": "svc_9f8e7d6c",
"created_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-01-15T11:00: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",
"type": "custom",
"is_primary": true,
"service_id": "svc_9f8e7d6c"
}Verify Domain
Run the DNS check for a custom domain.
POST /api/v1/tenants/{id}/domains/{domId}/verify
**Still pending:**
```json
{
"data": {
"status": "pending",
"verification_record": "staxa-verify=ver_a1b2c3d4",
"instructions": "Add a TXT record for _staxa-verify.app.acme.com with value: staxa-verify=ver_a1b2c3d4",
"server_ip": "65.108.x.x"
}
}
```
**Verified:**
```json
{
"data": {
"id": "dom_a1b2c3",
"tenant_id": "ten_a1b2c3d4",
"domain": "app.acme.com",
"type": "custom",
"status": "verified",
"verification_record": "staxa-verify=ver_a1b2c3d4",
"verified_at": "2026-01-15T11:00:00Z",
"ssl_status": "pending",
"is_primary": true,
"created_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-01-15T11:00:00Z"
}
}
```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",
"tenant_id": "ten_a1b2c3d4",
"provider_id": "prov_a1b2c3d4",
"direction": "inbound",
"protocol": "tcp",
"description": "Database access from office",
"target_service": "database",
"target_port": 5432,
"source_cidrs": ["203.0.113.0/24"],
"allocated_port": 30100,
"expires_at": "2026-01-16T10:30:00Z",
"status": "active",
"created_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-01-15T10:30:00Z"
}
]
}Create Network Rule
Create a network rule. Inbound TCP rules allocate a port; 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
Free-text description of the rule
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": "database",
"target_port": 5432,
"source_cidrs": ["203.0.113.0/24"],
"description": "Database access from office",
"ttl_hours": 24
}
```
**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 one network rule.
GET /api/v1/tenants/{id}/network-rules/{ruleId}
{
"data": {
"id": "nr_a1b2c3",
"tenant_id": "ten_a1b2c3d4",
"provider_id": "prov_a1b2c3d4",
"direction": "inbound",
"protocol": "tcp",
"description": "Database access from office",
"target_service": "database",
"target_port": 5432,
"source_cidrs": ["203.0.113.0/24"],
"allocated_port": 30100,
"expires_at": "2026-01-16T10:30:00Z",
"status": "active",
"created_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-01-15T10:30:00Z"
}
}Delete Network Rule
Delete a network rule. Inbound rules release the allocated port.
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 deployment events over 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
Stream 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 as they arrive Defaults to
false.
GET /api/v1/tenants/{id}/logs
curl "https://api.staxa.dev/api/v1/tenants/ten_a1b2c3d4/logs?lines=50&follow=true" \
-H "Authorization: Bearer sk_live_..."Templates
Pre-built application templates and the runtime versions you can build against.
List Templates
List available application templates for quick-start tenant creation.
GET /api/v1/templates
{
"data": [
{
"id": "tmpl_nextjs_postgres",
"slug": "nextjs-postgres",
"name": "Next.js + PostgreSQL",
"description": "Full-stack Next.js app with PostgreSQL database",
"framework": "nextjs",
"runtime": "node",
"runtime_version": "20",
"db_engine": "postgres",
"db_version": "16",
"default_env": {},
"default_size": "small",
"default_port": 3000,
"category": "fullstack",
"sort_order": 1,
"is_featured": true,
"is_active": true,
"created_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-01-15T10:30:00Z"
}
]
}Get Template
Get details for one template, by ID or slug.
GET /api/v1/templates/{id}
{
"data": {
"id": "tmpl_nextjs_postgres",
"slug": "nextjs-postgres",
"name": "Next.js + PostgreSQL",
"description": "Full-stack Next.js app with PostgreSQL database",
"framework": "nextjs",
"runtime": "node",
"runtime_version": "20",
"db_engine": "postgres",
"db_version": "16",
"default_env": {},
"default_size": "small",
"default_port": 3000,
"category": "fullstack",
"sort_order": 1,
"is_featured": true,
"is_active": true,
"created_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-01-15T10:30:00Z"
}
}List Runtime Versions
List the runtime versions the platform can build against.
GET /api/v1/config/runtimes
{
"data": {
"node": ["22", "20", "18"],
"python": ["3.13", "3.12", "3.11", "3.10"],
"go": ["1.24", "1.23", "1.22"],
"ruby": ["3.4", "3.3", "3.2"],
"java": ["23", "21", "17", "11"],
"php": ["8.4", "8.3", "8.2"],
"rust": ["1.83", "1.80", "1.77"],
"dotnet": ["9", "8"],
"elixir": ["1.17", "1.16", "1.15"],
"deno": ["2.1", "1.46"],
"bun": ["1.1", "1.0"]
}
}GitHub App
GitHub App installation and repository access.
Get GitHub Installation
Get the GitHub App installation status for the authenticated provider.
GET /api/v1/github/installation
**Not connected:**
```json
{
"data": {
"connected": false,
"install_url": "https://github.com/apps/staxa/installations/new"
}
}
```
**Connected:**
```json
{
"data": {
"connected": true,
"installation": {
"id": "ghi_a1b2c3d4",
"provider_id": "prov_a1b2c3d4",
"installation_id": 12345678,
"account_type": "Organization",
"account_login": "acme-org",
"account_id": 87654321,
"app_slug": "staxa",
"repository_selection": "selected",
"status": "active",
"created_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-01-15T10:30:00Z"
}
}
}
```Register GitHub Installation
Register a GitHub App installation for the provider. Called after GitHub redirects back from the install 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 through the GitHub App installation.
GET /api/v1/github/repos
{
"data": [
{
"id": 123456789,
"full_name": "acme-org/webapp",
"private": true,
"default_branch": "main",
"html_url": "https://github.com/acme-org/webapp"
}
]
}List GitHub Branches
List branches for one repository.
GET /api/v1/github/repos/{owner}/{repo}/branches
{
"data": [
{ "name": "main" },
{ "name": "develop" },
{ "name": "feature/auth" }
]
}Analyze Repository
Inspect a repository and detect its runtime, framework, and how to run it.
GET /api/v1/github/repos/{owner}/{repo}/analyze
curl "https://api.staxa.dev/api/v1/github/repos/acme-org/webapp/analyze?branch=main" \
-H "Authorization: Bearer sk_live_..."Registry Credentials
Private container registry authentication.
List Registries
List saved container registry credentials.
GET /api/v1/registries
{
"data": [
{
"id": "rcr_a1b2c3d4",
"provider_id": "prov_a1b2c3d4",
"name": "Docker Hub",
"registry_type": "dockerhub",
"registry_url": "https://index.docker.io/v1/",
"namespace": "acme",
"status": "active",
"last_verified": "2026-01-15T10:30:00Z",
"created_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-01-15T10:30:00Z"
}
]
}Create Registry Credential
Save credentials for a private container registry.
POST /api/v1/registries
**Docker Hub or GHCR:**
```json
{
"name": "Docker Hub",
"registry_type": "dockerhub",
"registry_url": "https://index.docker.io/v1/",
"namespace": "acme",
"credentials": {
"username": "acme",
"password": "dckr_pat_..."
}
}
```
**ECR:**
```json
{
"name": "Production ECR",
"registry_type": "ecr",
"registry_url": "https://123456789012.dkr.ecr.us-east-1.amazonaws.com",
"credentials": {
"aws_access_key_id": "AKIA...",
"aws_secret_access_key": "...",
"aws_region": "us-east-1"
}
}
```
**GCR or Artifact Registry:**
```json
{
"name": "GCR",
"registry_type": "gcr",
"registry_url": "https://gcr.io",
"credentials": {
"service_account_json": "{\"type\":\"service_account\",...}"
}
}
```Get Registry Credential
Get details for a saved registry credential. The credentials themselves are never returned.
GET /api/v1/registries/{credId}
{
"data": {
"id": "rcr_a1b2c3d4",
"provider_id": "prov_a1b2c3d4",
"name": "Docker Hub",
"registry_type": "dockerhub",
"registry_url": "https://index.docker.io/v1/",
"namespace": "acme",
"status": "active",
"last_verified": "2026-01-15T10:30:00Z",
"created_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-01-15T10:30:00Z"
}
}Update Registry Credential
Rename a registry credential or rotate its secret.
PATCH /api/v1/registries/{credId}
{
"name": "Docker Hub (rotated)",
"credentials": {
"username": "acme",
"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
Re-test that the saved credentials can still authenticate with the registry.
POST /api/v1/registries/{credId}/verify
{
"data": {
"id": "rcr_a1b2c3d4",
"provider_id": "prov_a1b2c3d4",
"name": "Docker Hub",
"registry_type": "dockerhub",
"registry_url": "https://index.docker.io/v1/",
"namespace": "acme",
"status": "active",
"last_verified": "2026-01-16T09:00:00Z",
"created_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-01-16T09:00:00Z"
}
}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",
"full_url": "index.docker.io/acme/webapp",
"description": "Frontend application",
"is_private": true,
"updated_at": "2026-01-14T18:02:00Z"
}
]
}List Registry Tags
List tags for one repository in the registry.
GET /api/v1/registries/{credId}/repos/{repo}/tags
{
"data": [
{
"name": "latest",
"digest": "sha256:abc123...",
"size_bytes": 52428800,
"pushed_at": "2026-01-14T18:02:00Z"
},
{
"name": "v1.2.3",
"digest": "sha256:def456...",
"size_bytes": 52428800,
"pushed_at": "2026-01-10T11:40:00Z"
}
]
}