API Reference
The PandaStack REST API is the programmatic control plane behind everything you can do in the dashboard and the CLI: create projects, provision databases, deploy edge functions, schedule cronjobs, wire up monitors, and manage your organization and billing.
Every resource is documented in this section, grouped by the thing it manages:
- Authentication — tokens, headers, the card wall, and rate limits
- Projects — static sites, container apps, deploys, build logs, domains, routing
- Databases — PostgreSQL and MySQL provisioning, credentials, backups, tiers
- Functions — edge functions and scheduled functions
- Cronjobs — scheduled container runs
- Metrics & analytics — performance metrics, audience analytics, live SSE streams
- Monitors — uptime monitors, heartbeats, alerts
- Organizations & teams — orgs, teams, members, invitations, account
- Usage & limits — plan allowances and metered usage
- Billing — plans, subscriptions, invoices, cards
- Integrations — GitHub App and the marketplace catalog
Base URL
Section titled “Base URL”All endpoints live under a single host and are versioned by path prefix:
https://api.pandastack.ioEvery documented route is prefixed with /v1 (for example /v1/projects), except the unauthenticated liveness probe /health and the two signature-verified webhook receivers (/webhook and /api/v1/webhook/github-events).
Your first request
Section titled “Your first request”The simplest authenticated call is GET /v1/me, which returns the current user and the organizations they belong to. It requires only a valid token — no active organization needs to be selected first.
GET /v1/me
curl https://api.pandastack.io/v1/me \ -H "Authorization: Bearer $PANDASTACK_TOKEN"{ "success": true, "data": { "id": 42, "email": "you@example.com", "username": "you", "firstName": "Ada", "lastName": "Lovelace", "avatarUrl": null, "mfaEnabled": false, "organizations": [ { "organizationId": "1907", "name": "Acme", "role": "owner", "cardVerified": true } ] }}See Authentication for how to obtain $PANDASTACK_TOKEN.
The response envelope
Section titled “The response envelope”Every API response — success or failure — is wrapped in a consistent envelope.
Success
Section titled “Success”A successful response always carries success: true and a data payload:
{ "success": true, "data": { /* resource */ } }A handful of write endpoints (deletes, mark-as-read, and async-triggering mutations) return a bare acknowledgement instead of a full resource:
{ "success": true }{ "success": true, "data": { "async": true } }A failed response carries success: false and an error object:
{ "success": false, "error": { "code": "bad_request", "message": "Human-readable, safe-to-display message.", "fields": { "name": "Name is required" }, "traceId": "8f3c2a1e-…" }}| Field | Type | Notes |
|---|---|---|
code | ErrorCode | One of the enum values in the table below. |
message | string | Safe, user-facing message. Never contains a stack trace. |
fields | record<string,string> | Optional. Per-field validation messages, keyed by field name. |
traceId | string | Optional. Correlates the error to server logs when you contact support. |
Error codes and HTTP status
Section titled “Error codes and HTTP status”The error.code enum maps one-to-one to an HTTP status code:
code | HTTP | Meaning |
|---|---|---|
bad_request | 400 | The request was malformed or failed validation (see fields). |
unauthorized | 401 | No credential, or the credential is invalid/expired. |
forbidden | 403 | Authenticated, but not allowed (role too low, or the user is banned). |
not_found | 404 | The resource does not exist, or you are not a member of the named org. |
conflict | 409 | The request conflicts with existing state (e.g. name already taken). |
quota_exceeded | 402 | A plan limit or the unverified-card wall was hit. |
rate_limited | 429 | You exceeded the request rate limit. |
internal | 500 | An unexpected server error. Retry; include traceId if it persists. |
Pagination
Section titled “Pagination”List endpoints return one of two paginated shapes, both nested inside data.
Cursor pagination
Section titled “Cursor pagination”Projects, deployments, and the activity feed use opaque-cursor pagination:
{ "success": true, "data": { "items": [ /* … */ ], "nextCursor": "eyJpZCI6MTIzfQ" }}Pass ?cursor=<nextCursor> on the next request to fetch the following page. When nextCursor is null, you have reached the end. Most cursor lists also accept ?limit= (capped at 100).
Page / total pagination
Section titled “Page / total pagination”Databases, functions, cronjobs, and their sub-lists use page-number pagination:
{ "success": true, "data": { "items": [ /* … */ ], "total": 137 }}Pass ?page= (1-based) and ?pageSize= (default 20, max 100).
Rate limits
Section titled “Rate limits”A global in-memory rate limit of 300 requests per minute applies, keyed by the last 16 characters of your bearer token (or by client IP when no token is present). /health is exempt.
When you exceed the limit you receive HTTP 429 with the standard error envelope:
{ "success": false, "error": { "code": "rate_limited", "message": "Too many requests" } }Versioning
Section titled “Versioning”The API is versioned in the URL path (/v1). New backward-compatible fields may be added to response bodies at any time, so clients should ignore unknown fields. Breaking changes ship under a new version prefix. Deprecated routes (for example POST /v1/databases/:name/upgrade) are kept for at least one release and documented as such.
Content types
Section titled “Content types”Send Content-Type: application/json for JSON bodies. A few endpoints accept multipart/form-data instead — static ZIP uploads (POST /v1/projects/static-upload) and function code uploads (POST /v1/functions). These are called out on their pages.
Webhooks
Section titled “Webhooks”Two endpoints receive signed callbacks from third parties and are not token-authenticated — they verify a cryptographic signature instead. They are documented on the pages of the features they drive:
- Stripe →
POST /webhook - GitHub App →
POST /api/v1/webhook/github-events
Their responses are not enveloped.