Add API Endpoint
OpenAPI-first. The spec at openapi/v1.yaml is the single source of truth — types, scopes, schemas, and CLI surfaces all derive from it.
Default stance
Edit openapi/v1.yaml first, then implement. Implementing first and back-filling the spec is how endpoints drift from documentation, scopes get inconsistent, and the SDK breaks. The spec is also what verify-openapi-routes.py and verify-scope-sync.py enforce — go out of order and the gates will block your PR.
Route handlers should be thin. Auth, scope checks, and request shape live in withApiKeyAuth. Business logic lives in lib/<resource>.ts (queries) or lib/actions/<resource>.ts (mutations). If your route handler has more than ~30 lines of logic, you're probably putting business logic in the wrong layer.
Use this skill when
Adding or changing public API routes under /api/v1, adding a new scope, restructuring auth/permissions on an existing endpoint, or designing the API surface for a new resource.
New endpoint checklist
- Define in OpenAPI spec — add path, request/response schemas, and
x-required-scopesinopenapi/v1.yaml. - Create route handler —
app/api/v1/<resource>/route.ts. - Auth — wrap in
withApiKeyAuthfromlib/api-v1-auth.ts. - Scopes — pass
requiredScopesto the wrapper;hasScopes()fromlib/scopes.tsdoes the work. - Business logic — queries in
lib/<resource>.ts, mutations inlib/actions/<resource>.ts. - Database — if new tables, edit
lib/schema.tsand runbunx drizzle-kit generate(seedb-health). - Tests —
tests/unit/. - CLI command —
cli/cmd/<resource>.go(seecli-development). - Webhook events — emit any meaningful state change with
sendWebhookEventAsync(seewebhooks-and-events). - Gates —
./scripts/gates.sh. - SDK — if spec changed, regenerate via
bun run generate:sdkand followrelease-sdkfor publication.
Route handler pattern
import { NextRequest, NextResponse } from "next/server"
import { withApiKeyAuth } from "@/lib/api-v1-auth"
export const GET = withApiKeyAuth(
async (_request: NextRequest, { apiKey, user, organizationId }) => {
const result = await listResources(organizationId)
return NextResponse.json({ data: result })
},
{ requiredScopes: ["resource:read"] }
)
The wrapper validates the API key, resolves user + organization from it, enforces scopes, and rejects with the right status codes if any check fails. Don't reimplement any of that in the handler.
Adding a new scope
- Add to
lib/scopes.tsinALL_SCOPESandSCOPE_DESCRIPTIONS. - Add to
openapi/v1.yamlunderx-all-scopesandcomponents.schemas.Scope.enum. - Add
x-required-scopesto the endpoints that need it. - Enforce via
requiredScopesinwithApiKeyAuth. - Tests + gates.
verify-scope-sync.py will fail the build if these get out of sync.
Architecture layers
openapi/v1.yaml ← spec (source of truth)
app/api/v1/*/route.ts ← thin route handler (auth + dispatch)
lib/api-v1-auth.ts ← withApiKeyAuth wrapper
lib/scopes.ts ← scope definitions
lib/<resource>.ts ← queries
lib/actions/<resource>.ts ← mutations
tests/unit/ ← tests
cli/cmd/<resource>.go ← CLI command
Hard rules
- Don't edit
sdks/typescript/src/types.tsmanually. It's generated. - Don't add scopes only in code or only in spec — they must match. The verifier will fail your PR.
- Don't write fat route handlers. Push logic into
lib/. - Don't introduce a new auth path. All v1 endpoints go through
withApiKeyAuth. If you need session auth, that's a non-v1 endpoint and lives elsewhere (/api/webhooks/portal/route.tsis one example).
Current scopes
| Scope | Grants |
|---|---|
keys:read / keys:write | API keys |
usage:read / usage:write | Usage metrics |
webhooks:read / webhooks:write | Events list, SSE, test events, portal |
Where things live
| File | Purpose |
|---|---|
openapi/v1.yaml | Spec — edit this first |
app/api/v1/ | Route handlers |
lib/api-v1-auth.ts | withApiKeyAuth |
lib/scopes.ts | Scope definitions |
lib/api-keys.ts | Key validation, scope checks |
scripts/verify-openapi-routes.py | Gate: every spec path has a handler |
scripts/verify-scope-sync.py | Gate: scopes match across spec + code |
openapi/AGENTS.md | Spec editing rules |
docs/api.md | Full endpoint reference |
Auxiliary content
- references/original-guide.md — full canonical guide with the route handler pattern, current scopes, and architecture layers
- references/graph.md — handoff to
db-health,release-sdk,webhooks-and-events - scripts/check-api-surface.sh — enumerates current endpoints, scopes, and CLI coverage; run before adding a new route to spot existing patterns
- assets/endpoint-plan-template.md — fill-in template for an endpoint design (path, scopes, request/response, error cases)
- assets/evals/basic.json — eval cases for skill regression