---
title: REST API
path: reference/rest-api
status: published
---

# REST API

The ScaiFlow backend exposes a tenant-scoped REST API at `/api/v1/*` on whatever hostname your tenant deploys it under.

This is the public surface — the canvas uses it, your CI can use it, third-party tools can use it. Every endpoint that mutates requires ScaiKey authentication (or the deprecated dev-token fallback); read endpoints obey per-flow ACLs.

## Base URL

```text
https://scaiflow.<your-domain>/api/v1
```

Substitute `<your-domain>` with whatever your tenant deploys at (`scaiflow.scailabs.ai`, `scaiflow.acme.example`, etc.).

## Authentication

Two auth flows are supported:

### ScaiKey OIDC (primary)

Bearer token from the BFF token exchange:

```http
Authorization: Bearer <access_token>
```

To obtain a token from a service that already has a refresh token, use `POST /v1/auth/refresh` (see [Auth router](./rest-api/auth)). For interactive browser logins, the canvas runs the OAuth Authorization Code + PKCE flow against ScaiKey directly and exchanges the code via `POST /v1/auth/exchange`.

### Dev token (deprecated, only for `POST /v1/dev/deploy`)

Shared bearer set via `SCAIFLOW_API_TOKEN` env at the API process. Only the legacy `/v1/dev/deploy` endpoint honors it. New code should use ScaiKey OIDC.

## Routers

The API is split into routers by domain. Each router page lists every endpoint with method, path, request, response, and required permissions.

| Router | Surface | Page |
|---|---|---|
| Flows | flow CRUD, deploy, run-tests, versions, presence, deployments | [./rest-api/flows](./rest-api/flows) |
| Auth | runtime config, BFF token exchange + refresh, `/v1/me` | [./rest-api/auth](./rest-api/auth) |
| Admin | ScaiKey-driven user/group sync, super_admin grants, tenant views | [./rest-api/admin](./rest-api/admin) |
| Catalog | per-tenant flow packages: list, publish, install, visibility | [./rest-api/catalog](./rest-api/catalog) |
| Tenants | per-tenant ScaiGrid credentials | [./rest-api/tenants](./rest-api/tenants) |
| Lookups | read-only proxies into ScaiQueue / ScaiBunker / ScaiGrid | [./rest-api/lookups](./rest-api/lookups) |
| ScaiCore | events, checkpoints, invoke proxy | [./rest-api/scaicore](./rest-api/scaicore) |
| Flow ACLs | per-flow access control: list / grant / revoke | [./rest-api/flow-acls](./rest-api/flow-acls) |
| Preview | live compile + validate (no persistence) | [./rest-api/preview](./rest-api/preview) |
| Webhooks | ScaiKey signed-webhook receiver | [./rest-api/webhooks](./rest-api/webhooks) |
| Deploy (legacy) | `POST /v1/dev/deploy` and `POST /v1/flows/{id}/deploy` | [./rest-api/deploy](./rest-api/deploy) |

## Permissions

Two canonical permissions, inherited from [ScaiCore](/docs/scaicore):

| Permission | Required for |
|---|---|
| `scaicore:view` | All read endpoints, live preview, validation, browsing catalog, watching events. |
| `scaicore:manage` | All mutating endpoints, deploy, run-tests, publish catalog, resolve checkpoints, invoke. |

Plus two ScaiFlow-local admin roles (stored in the ScaiFlow DB, not in ScaiKey):

- `tenant_admin` — tenant-wide visibility + tenant credential management.
- `super_admin` — platform-wide.

## Error shape

Errors follow the FastAPI default `{"detail": ...}` shape. Some endpoints return a structured detail dict for machine consumption:

```jsonc
// 412 — missing tenant credentials
{
  "detail": {
    "error": "missing_credentials",
    "message": "no ScaiGrid credentials for tenant 'acme'",
    "fix": "Sign in with ScaiKey, or set this tenant's API key via POST /v1/tenants/me/scaigrid_credentials."
  }
}
```

```jsonc
// 502 — upstream ScaiGrid auth rejected
{
  "detail": {
    "error": "scaigrid_auth",
    "message": "...",
    "fix": "Caller needs `scaicore:view` on this tenant."
  }
}
```

## Versioning

The API is at `/v1`. There is no `/v2` yet. Breaking changes will bump to `/v2`; additive changes (new fields, new endpoints) land on `/v1`.

## Rate limiting

No rate limiting is enforced by ScaiFlow itself today. Upstream (ScaiGrid) may rate-limit per its own policy; ScaiFlow returns the upstream's rate-limit response unchanged.

## CORS

The canvas runs on `http://localhost:5173` in dev and on its production host in prod. The API allows those origins by default; check `apps/api/scaiflow_api/app.py` for the exact list if you need to add another.
