Skip to main content

API Reference

The REST reference is rendered from apps/docs/openapi.yaml at /api/.

How the spec is maintained

The spec is partially generated and drift-gated — not fully generated, and not purely handwritten either:

  • The /projects* and /runs* paths are generated from route code: the zod-openapi route definitions in apps/api/src/routes/projects.ts and apps/api/src/routes/runs.ts are the same objects the runtime handlers register against, so those paths cannot drift from the implementation.
  • Every other path is maintained by hand in apps/docs/openapi.base.yaml (the schemas are derived from the Zod schemas in packages/shared/src/schemas and the route modules).
  • pnpm --filter @qa-clone/api openapi:export merges both halves deterministically into apps/docs/openapi.yaml.
  • CI drift gate: the vitest suite (apps/api/__tests__/openapi.test.ts) re-assembles the spec from its sources and fails when the committed openapi.yaml is stale, when a mounted public route has no path entry, or when the spec documents a route that is not mounted. See docs/runbooks/openapi-drift.md.

Envelope

All JSON error responses use:

{ "ok": false, "error": "human-readable message" }

Success bodies are either the documented resource shape or:

{ "ok": true, "data": { } }

Authentication

Two bearer credentials are accepted on Authorization:

1. Clerk session JWT (browser / dashboard)

Org-scoped endpoints require a Clerk JWT of a user with an active organization — org and role are resolved from the verified token, never from headers.

curl https://api.testsbot.com/projects \
-H "Authorization: Bearer <clerk-session-jwt>"

2. API keys (tb_live_…) — CI and integrations

Create a key under Settings → API Keys (or POST /api-keys, admin-only). The plaintext key is shown exactly once at creation. Scopes:

  • read — GET requests only
  • read_write — full access (acts as admin)
# Trigger a run from CI with an API key
curl -X POST https://api.testsbot.com/runs \
-H "Authorization: Bearer tb_live_0123456789abcdef0123456789abcdef" \
-H "Content-Type: application/json" \
-d '{ "test_case_id": "00000000-0000-0000-0000-000000000000" }'

Public, unauthenticated endpoints: GET /health, GET /status/incidents, GET /legal/{doc}.

See the interactive reference at /api/ for request/response schemas and try-it examples.