Projects API

API endpoints for project management.

Manage Supabase projects through the REST API.

Endpoints Overview

MethodEndpointDescription
GET/projectsList all projects
POST/projectsCreate project
POST/projects/createCreate with streaming
GET/projects/:idGet project
DELETE/projects/:idDelete project
GET/projects/:id/statusGet status
POST/projects/:id/startStart project
POST/projects/:id/stopStop project
GET/projects/:id/logsGet logs
GET/projects/:id/containersGet containers
GET/projects/:id/servicesGet services
PUT/projects/:id/servicesUpdate services
GET/projects/:id/domainGet domain
POST/projects/:id/domainSet domain
DELETE/projects/:id/domainRemove domain
GET/projects/:id/settingsGet settings
PUT/projects/:id/settingsUpdate settings
GET/projects/:id/auth-providersGet auth providers
PUT/projects/:id/auth-providersUpdate auth providers
GET/projects/:id/functionsList Edge Functions
POST/projects/:id/functionsCreate Edge Function
GET/projects/:id/functions/:nameGet Edge Function files
PUT/projects/:id/functions/:nameWrite / delete Edge Function files
DELETE/projects/:id/functions/:nameDelete Edge Function

List Projects

GET /api/v1/projects

Permission: projects:read

Response:

[
  {
    "id": "my-project",
    "name": "My Project",
    "description": "Development environment",
    "directory": "/var/supascale/projects/my-project",
    "status": "running",
    "ports": {
      "api": 8000,
      "db": 5432,
      "studio": 3001
    },
    "domain": "api.example.com",
    "createdAt": "2026-01-19T10:00:00Z",
    "updatedAt": "2026-01-19T12:00:00Z"
  }
]

Create Project

POST /api/v1/projects

Permission: projects:write

Request:

{
  "id": "my-project",
  "name": "My Project",
  "description": "Development environment",
  "postgresVersion": "15"
}

Request Parameters:

ParameterTypeRequiredDescription
idstringYesUnique project identifier (lowercase, alphanumeric, hyphens, underscores)
namestringYesDisplay name for the project
descriptionstringNoOptional description
postgresVersionstringNoPostgreSQL version: "15", "16", or "17" (default: "15")

Response:

{
  "id": "my-project",
  "name": "My Project",
  "directory": "/var/supascale/projects/my-project",
  "ports": { "api": 8000, "db": 5432, "studio": 3001 },
  "postgresVersion": "15",
  "externalDbAccess": false,
  "status": "running",
  "createdAt": "2026-01-19T12:00:00Z"
}

Create Project (Streaming)

POST /api/v1/projects/create

Returns Server-Sent Events with progress updates.

Request:

{
  "id": "my-project",
  "name": "My Project",
  "services": ["realtime", "storage"],
  "postgresVersion": "17"
}

Request Parameters:

ParameterTypeRequiredDescription
idstringYesUnique project identifier
namestringYesDisplay name for the project
descriptionstringNoOptional description
servicesarrayNoOptional services to enable (e.g., ["realtime", "storage", "inbucket"])
postgresVersionstringNoPostgreSQL version: "15", "16", or "17" (default: "15")

Response (SSE):

event: progress
data: {"step": 1, "total": 7, "status": "running", "message": "Validating..."}

event: progress
data: {"step": 2, "total": 7, "status": "running", "message": "Generating config..."}

event: progress
data: {"step": 6, "total": 7, "status": "completed", "detail": "7 services configured (PostgreSQL 17)"}

event: complete
data: {"project": {...}}

Get Project

GET /api/v1/projects/:id

Query Parameters:

  • credentials=true - Include decrypted credentials

Response:

{
  "id": "my-project",
  "name": "My Project",
  "description": "Development environment",
  "directory": "/var/supascale/projects/my-project",
  "status": "running",
  "ports": { "api": 8000, "db": 5432, "studio": 3001 },
  "domain": null,
  "enabledServices": ["db", "rest", "auth", "kong", "studio", "meta", "realtime"],
  "postgresVersion": "15",
  "externalDbAccess": false,
  "autoStart": true,
  "createdAt": "2026-01-19T10:00:00Z"
}

With credentials=true:

{
  "...": "...",
  "credentials": {
    "dbPassword": "secure-password",
    "jwtSecret": "jwt-secret-key",
    "anonKey": "eyJhbGciOiJIUzI1NiIs...",
    "serviceRoleKey": "eyJhbGciOiJIUzI1NiIs..."
  }
}

Delete Project

DELETE /api/v1/projects/:id

Query Parameters:

  • removeFiles=true - Delete project files

Response:

{
  "success": true,
  "message": "Project deleted"
}

Get Project Status

GET /api/v1/projects/:id/status

Response:

{
  "status": "running",
  "lastChecked": "2026-01-19T12:00:00Z",
  "containers": {
    "total": 8,
    "running": 8,
    "stopped": 0
  }
}

Start Project

POST /api/v1/projects/:id/start

Returns SSE with startup logs.

Response:

event: log
data: {"message": "Starting containers..."}

event: log
data: {"message": "Container db started"}

event: complete
data: {"success": true}

Stop Project

POST /api/v1/projects/:id/stop

Returns SSE with shutdown logs.

Get Project Logs

GET /api/v1/projects/:id/logs

Query Parameters:

  • service - Filter by service name
  • tail - Number of lines (default: 200)

Response:

{
  "logs": [
    {
      "timestamp": "2026-01-19T12:00:00Z",
      "service": "db",
      "level": "info",
      "message": "database system is ready"
    }
  ]
}

Get Containers

GET /api/v1/projects/:id/containers

Query Parameters:

  • stats=true - Include CPU/memory stats

Response:

{
  "containers": [
    {
      "name": "my-project-db",
      "service": "db",
      "status": "running",
      "state": "Up 2 hours"
    }
  ]
}

With stats=true:

{
  "containers": [
    {
      "name": "my-project-db",
      "service": "db",
      "status": "running",
      "stats": {
        "cpu": "2.5%",
        "memory": "256MB / 1GB"
      }
    }
  ]
}

Get/Update Services

GET /api/v1/projects/:id/services
PUT /api/v1/projects/:id/services

GET Response:

{
  "services": ["db", "rest", "auth", "kong", "studio", "meta", "realtime"],
  "enabledServices": {
    "db": true,
    "rest": true,
    "auth": true,
    "kong": true,
    "studio": true,
    "meta": true,
    "realtime": true,
    "storage": false,
    "functions": false,
    "inbucket": false
  }
}

PUT Request:

{
  "services": ["db", "rest", "auth", "kong", "studio", "meta", "realtime", "storage"]
}

Domain Configuration

GET /api/v1/projects/:id/domain
POST /api/v1/projects/:id/domain
DELETE /api/v1/projects/:id/domain

POST Request:

{
  "name": "api.example.com",
  "sslEnabled": true,
  "webServer": "nginx"
}

POST Response:

{
  "success": true,
  "data": {
    "name": "api.example.com",
    "sslEnabled": true,
    "webServer": "nginx"
  },
  "message": "Domain configured successfully. nginx configuration generated. Restart the project for Studio to use the new domain.",
  "requiresRestart": true
}

When a domain is configured, the project's API_EXTERNAL_URL is automatically updated. You must restart the project for Supabase Studio to use the new domain for API calls.

Project Settings

GET /api/v1/projects/:id/settings
PUT /api/v1/projects/:id/settings

GET Query Parameters:

  • category - storage, smtp, auth, pooler, api, database

Categories:

CategoryDescription
storageFile storage backend settings (local/S3)
smtpEmail/SMTP server settings
authAuthentication settings (signup, JWT, etc.)
poolerConnection pooler (Supavisor) settings
apiPostgREST API settings
databasePostgreSQL version and external access settings
inbucketEmail testing (Inbucket) settings

SMTP Settings Example

PUT Request:

{
  "category": "smtp",
  "settings": {
    "host": "smtp.example.com",
    "port": 587,
    "user": "apikey",
    "password": "your-api-key"
  }
}

Database Settings

Configure which network interface the PostgreSQL ports listen on. The address applies to both the direct port (port) and the Supavisor pooler port (poolerPort).

GET Response:

{
  "success": true,
  "data": {
    "externalAccess": false,
    "bindAddress": "127.0.0.1",
    "postgresVersion": "15",
    "port": 54322,
    "poolerPort": 54329,
    "interfaces": [
      { "address": "203.0.113.5", "interfaceName": "eth0" }
    ],
    "firewall": {
      "type": "ufw",
      "active": true,
      "portsOpen": true,
      "portDetails": [
        { "port": 54322, "protocol": "tcp", "allowed": true },
        { "port": 54329, "protocol": "tcp", "allowed": true }
      ]
    }
  }
}

PUT Request - Bind to an Address:

{
  "category": "database",
  "settings": {
    "bindAddress": "0.0.0.0"
  }
}

bindAddress must be 127.0.0.1, 0.0.0.0, or an address from the GET response's interfaces list; anything else is rejected with 400. The legacy "externalAccess": true / false form is still accepted and maps to 0.0.0.0 / 127.0.0.1.

Response:

{
  "success": true,
  "message": "Database settings updated. PostgreSQL published on 0.0.0.0: direct port 54322, pooler port 54329. Firewall ports opened. Restart the project for changes to take effect.",
  "requiresRestart": true
}

External PostgreSQL Access: Any address other than 127.0.0.1 exposes your PostgreSQL ports to that network. Ensure your firewall is properly configured and use strong database passwords.

PostgreSQL Version: postgresVersion is fixed when the project is created. Sending a different value returns 400; create a new project and migrate your data to change major versions.

Inbucket Settings

Inbucket is a local email testing server that captures all outgoing emails for development and testing.

GET Response:

{
  "success": true,
  "data": {
    "enabled": true,
    "externalAccess": false
  }
}

PUT Request - Enable Inbucket:

{
  "category": "inbucket",
  "settings": {
    "enabled": true
  }
}

PUT Request - Enable External Access:

{
  "category": "inbucket",
  "settings": {
    "externalAccess": true
  }
}

Response:

{
  "success": true,
  "message": "Inbucket settings updated. External access enabled on port 54324. Restart the project for changes to take effect.",
  "requiresRestart": true
}

About Inbucket: When Inbucket is enabled, Supabase Auth is automatically configured to send emails to the local Inbucket SMTP server instead of external providers. This allows you to test email flows (signup confirmations, password resets, magic links) without sending real emails.

External Access: Enabling external access allows anyone on your network to view captured emails. This is safe for development but should not be enabled in production environments.

Auth Providers

GET /api/v1/projects/:id/auth-providers
PUT /api/v1/projects/:id/auth-providers

PUT Request:

{
  "providers": [
    {
      "provider": "google",
      "enabled": true,
      "clientId": "your-client-id",
      "clientSecret": "your-client-secret"
    }
  ]
}

Edge Functions

GET    /api/v1/projects/:id/functions
POST   /api/v1/projects/:id/functions
GET    /api/v1/projects/:id/functions/:name
PUT    /api/v1/projects/:id/functions/:name
DELETE /api/v1/projects/:id/functions/:name

Permission: projects:read for GET, projects:write for POST / PUT / DELETE

Functions live in supabase/docker/volumes/functions/<name>/ inside the project directory and are served by the functions container at /functions/v1/<name> through the API gateway. Changes take effect on the next request — no restart. The main directory is the runtime's router and is never listed or writable through this API.

Function names use lowercase letters, digits, - and _ (1–63 characters, starting with a letter or digit).

List — GET Response:

{
  "success": true,
  "data": {
    "serviceEnabled": true,
    "functions": [
      {
        "name": "hello",
        "hasEntrypoint": true,
        "fileCount": 1,
        "sizeBytes": 412,
        "modifiedAt": "2026-08-28T14:02:11.000Z"
      }
    ]
  }
}

Create — POST Request:

{
  "name": "send-welcome-email",
  "content": "Deno.serve(() => new Response('ok'))"
}

content is the index.ts entrypoint; omit it to start from the built-in template. Returns 201, or 409 if the name is taken.

Get — GET Response:

{
  "success": true,
  "data": {
    "name": "send-welcome-email",
    "files": [
      { "path": "index.ts", "sizeBytes": 412, "content": "Deno.serve(...)" },
      { "path": "lib/template.ts", "sizeBytes": 88, "content": "export const html = ..." },
      { "path": "assets/logo.png", "sizeBytes": 20480 }
    ]
  }
}

content is included for text files up to 1 MB. Binary or larger files are listed without it.

Update — PUT Request:

{
  "files": [
    { "path": "index.ts", "content": "Deno.serve(...)" },
    { "path": "lib/template.ts", "content": "..." }
  ],
  "deletePaths": ["old-helper.ts"]
}

Paths are relative to the function directory; anything that would resolve outside it is rejected with 400. Parent directories are created as needed. The response carries the updated file list.

Delete — DELETE Response:

{
  "success": true,
  "message": "Function \"send-welcome-email\" deleted."
}

Invoke a function:

curl "http://your-server:54321/functions/v1/send-welcome-email" \
  -H "Authorization: Bearer <anon or service_role key>" \
  -H "Content-Type: application/json" \
  -d '{"name": "Ada"}'

Bulk upload: to bring in a whole supabase/functions directory at once, use Import Edge Functions with a .tar.gz or .zip archive.