--- name: Public API with Organization-Scoped Automation Keys overview: Build the public API on top of the shipped organization automation foundation: the `/org/[slug]/automation` section already exists, MCP is live, `/automation/api` and `/automation/zapier` exist as placeholder pages, and org-scoped automation keys already exist in the backend. This PRD updates the remaining work to ship the actual public API experience and docs. todos: - id: confirm-existing-foundation content: Confirm and preserve the shipped automation foundation (`Automation` nav, `/org/[slug]/automation` redirect, MCP page, organization automation key backend) status: completed - id: org-automation-foundation content: Keep using `organization_api_key` and `/organization/automation/*` as the shared automation-key foundation for MCP, API, and Zapier status: completed - id: mcp-page-reference content: Use the existing MCP implementation as the structural reference for the API page (route shell, server load, feature API class, feature page component, translations) status: completed - id: agents-guidance content: Ensure all implementation in this initiative follows `AGENTS.md` status: completed - id: api-tab-page content: Replace the placeholder `/org/[slug]/automation/api` page with a real API page that follows the MCP page pattern status: completed - id: api-tab-server-load content: Add `/org/[slug]/automation/api/+page.server.ts` to load API key and usage data using the existing organization automation endpoints status: completed - id: api-tab-copy content: Add API-specific translations and content for docs links, usage messaging, and key management states status: completed - id: v1-validation-schemas content: Add public API input/output validation schemas on top of the existing automation-key schemas status: completed - id: v1-services content: Create versioned public API services for courses and audience/students status: completed - id: v1-routes content: Create and register `/v1` public API routes authenticated by organization automation keys status: completed - id: public-api-scopes content: Finalize the public API scope model and middleware requirements for `api` and `zapier` automation keys status: completed - id: tests content: Add API tests for automation auth, `/v1` routes, and contract coverage status: completed - id: openapi-contracts content: Ensure all public API routes are represented in generated OpenAPI output status: completed - id: openapi-publish content: Regenerate and upload the latest OpenAPI spec via `upload-openapi-spec.ts` status: completed - id: docs-openapi-integration content: Add the public API docs experience in `apps/docs` status: completed - id: build-verify content: Build and verify API and docs packages after implementation status: completed isProject: false --- # Public API with Organization-Scoped Automation Keys ## Status - Done (shipped) ## Goal Ship a versioned public API on top of the existing organization automation platform. The current codebase already supports shared org-scoped automation keys and a dedicated Automation section in `/org`; this PRD now focuses on the remaining API product work rather than re-specifying the already-shipped foundation. ## Implementation Guardrails - All implementation in this initiative must follow [AGENTS.md](../../AGENTS.md). - Do not design this as a brand-new standalone automation surface. Follow the existing patterns already used by the MCP implementation. - For dashboard work, the API page must mirror the MCP structure: - route shell under `apps/dashboard/src/routes/org/[slug]/automation/*` - server-side data loading in `+page.server.ts` - state and mutations in `apps/dashboard/src/lib/features/automation/api/automation.svelte.ts` - request/response types in `apps/dashboard/src/lib/features/automation/utils/types.ts` - page composition in `apps/dashboard/src/lib/features/automation/pages/api.svelte` - Do not define new dashboard types inside `.svelte.ts` files. Keep feature types in `utils/types.ts` and infer them from the API client. - Keep user-facing copy in `apps/dashboard/src/lib/utils/translations/en.json`. ## Current State Confirmed in Code ### Org navigation and routes These pieces already exist and should be treated as the current baseline: - `apps/dashboard/src/lib/features/ui/navigation/org-navigation.ts` - contains an admin-only `Automation` section under `/org/[slug]/automation` - exposes nested entries for `MCP`, `Zapier`, and `API` - `apps/dashboard/src/routes/org/[slug]/automation/+page.ts` - redirects `/org/[slug]/automation` to `/org/[slug]/automation/mcp` - `apps/dashboard/src/routes/org/[slug]/automation/mcp/+page.svelte` - shipped MCP route shell using `Page.Root`, `Page.Header`, and `Page.Body` - `apps/dashboard/src/routes/org/[slug]/automation/mcp/+page.server.ts` - already loads automation keys and usage for `type=mcp` - `apps/dashboard/src/routes/org/[slug]/automation/api/+page.svelte` - already exists, but is currently a placeholder - `apps/dashboard/src/routes/org/[slug]/automation/zapier/+page.svelte` - already exists, but is currently a placeholder ### Frontend automation feature foundation These pieces already exist and should be reused: - `apps/dashboard/src/lib/features/automation/api/automation.svelte.ts` - shared automation API class for list/create/revoke/rotate - `apps/dashboard/src/lib/features/automation/utils/types.ts` - shared request and response inference types - `apps/dashboard/src/lib/features/automation/pages/mcp.svelte` - the current reference implementation for page structure and composition - `apps/dashboard/src/lib/features/automation/pages/api.svelte` - currently renders only a coming-soon callout - `apps/dashboard/src/lib/utils/translations/en.json` - already contains automation navigation and MCP copy ### Backend automation-key foundation This work is already implemented and should not be re-invented as a separate `/api-key` system: - `packages/db/src/schema.ts` - already defines `organization_api_key` - already defines `organization_automation_usage` - `packages/utils/src/validation/organization/automation-key.ts` - already defines key types: `mcp`, `api`, `zapier` - already defines scopes, query params, creation payload, and param validation - `packages/db/src/queries/organization/automation-key.ts` - already handles persistence for organization automation keys - `apps/api/src/services/organization/automation-key.ts` - already handles secret generation, hashing, creation, revoke, rotate, and auth - `apps/api/src/routes/organization/automation.ts` - already exposes: - `GET /organization/automation/usage` - `GET /organization/automation/keys` - `POST /organization/automation/keys` - `DELETE /organization/automation/keys/:keyId` - `POST /organization/automation/keys/:keyId/rotate` ### Important correction to the old PRD The old PRD assumed: - a brand-new Automation page still needed to be created - Automation should be one page with internal tabs - a standalone `/api-key` backend should be introduced - `Webhooks` was one of the primary tabs That is no longer accurate. The current product structure is: - org navigation owns the Automation section - Automation is split into nested routes: `mcp`, `zapier`, and `api` - the shared backend lives under `/organization/automation/*` - MCP is the shipped reference page - `api` and `zapier` are present as placeholder pages ## Existing Foundation to Build On ### 1. Shared Automation Keys The public API must reuse the existing organization-scoped automation-key model. Current model: - table: `organization_api_key` - key types: `mcp`, `api`, `zapier` - key prefixes: generated from the actual secret prefix - auth: bearer token via the existing automation-key service/middleware path - rotation and revocation: already implemented - usage tracking: already implemented in `organization_automation_usage` Design requirement: - do not introduce a second key table or a second dashboard key-management surface - do not reintroduce the older `/api-key` route design from the original PRD - build the public API on the existing `organization/automation` contracts unless there is a deliberate migration plan ### 2. Dashboard Information Architecture The Automation product is already organized as nested org routes, not a tabbed single-page shell. Required route structure: - `/org/[slug]/automation` - redirect only - `/org/[slug]/automation/mcp` - existing reference implementation - `/org/[slug]/automation/api` - target page for public API setup and management - `/org/[slug]/automation/zapier` - separate placeholder or future implementation Design requirement: - keep the org sidebar/navigation pattern unchanged - do not replace nested routes with a new internal tabs container for top-level Automation navigation - if tabs are used inside a page, use them only for local subcontent, the way MCP uses setup tabs for client snippets ## Remaining Product Scope ### 3. API Page in Dashboard The next dashboard milestone is to replace the placeholder API page with a real implementation. ### Target route files - `apps/dashboard/src/routes/org/[slug]/automation/api/+page.svelte` - `apps/dashboard/src/routes/org/[slug]/automation/api/+page.server.ts` - `apps/dashboard/src/lib/features/automation/pages/api.svelte` - `apps/dashboard/src/lib/features/automation/api/automation.svelte.ts` - `apps/dashboard/src/lib/features/automation/utils/types.ts` - `apps/dashboard/src/lib/utils/translations/en.json` ### Required structure The API page must follow the same structural pattern already used by MCP: 1. Route shell: - use `Page.Root`, `Page.Header`, `Page.HeaderContent`, `Page.Title`, and `Page.Body` - set the page title from `automation.tabs.api` - use an API-specific subtitle, not the generic coming-soon copy 2. Server load: - add `+page.server.ts` - load key data with `classroomio.organization.automation.keys.$get({ query: { type: 'api' } })` - load usage data with `classroomio.organization.automation.usage.$get({ query: { type: 'api' } })` - hydrate the shared `automationApi` store the same way MCP currently does 3. Feature page: - keep composition in `apps/dashboard/src/lib/features/automation/pages/api.svelte` - mirror the MCP page layout style and sectioning - do not leave this page as a single callout ### Required API page sections The real API page should include the following, using the MCP page as the UX baseline: - admin permission callout for non-admin users - usage cards or equivalent summary block for API-specific limits - API key table filtered to `type === 'api'` - create key flow - rotate key flow - revoke key flow - one-time secret reveal after creation or rotation - docs/discovery section with a link to the public API documentation - API-specific setup guidance ### Reuse requirements - reuse `automationApi` instead of introducing a separate API-only dashboard client - reuse shared automation types in `utils/types.ts` - add new helper utilities only if the API page needs API-specific setup snippets or docs helpers ### 4. Public API Routes The actual public API is still pending. ### Router structure Create: - `apps/api/src/routes/v1/index.ts` - `apps/api/src/routes/v1/courses.ts` - `apps/api/src/routes/v1/audience.ts` Register in `apps/api/src/app.ts` as: ```typescript .route('/public-api/v1', v1Router) ``` ### Authentication model All `/public-api/v1` routes must authenticate with organization automation keys. Requirements: - use the existing automation-key authentication path - require `type: 'api'` or an equivalent scope model that intentionally permits public API access - document and enforce scopes clearly - do not rely on session auth for `/public-api/v1` ### Planned endpoint groups Initial public API surface: | Method | Path | Description | | ------ | ---- | ----------- | | GET | `/public-api/v1/courses` | List organization courses | | POST | `/public-api/v1/courses` | Create a course | | GET | `/public-api/v1/courses/:courseId` | Get course details | | PUT | `/public-api/v1/courses/:courseId` | Update a course | | DELETE | `/public-api/v1/courses/:courseId` | Soft-delete a course | | GET | `/public-api/v1/courses/:courseId/export` | Export course content as JSON | | GET | `/public-api/v1/courses/:courseId/students` | List enrolled students | | GET | `/public-api/v1/audience` | List organization audience members | | POST | `/public-api/v1/audience` | Add a student to the organization | | POST | `/public-api/v1/audience/assign-courses` | Assign one or more audience members to one or more courses | | GET | `/public-api/v1/audience/:studentId` | Get student details | | PUT | `/public-api/v1/audience/:studentId` | Update student details | | DELETE | `/public-api/v1/audience/:studentId` | Remove a student from the organization | Enrollment decision: - keep only the audience assignment endpoint for enrollment - do not add a separate course-centric enrollment route such as `POST /public-api/v1/courses/:courseId/students` - a single audience member plus a single course in `POST /public-api/v1/audience/assign-courses` covers the single-enrollment case - multiple audience members and multiple courses in the same payload cover the bulk-assignment case ### 5. Validation and Services Add public API schemas and services on top of the existing automation-key foundation. Target files: - `packages/utils/src/validation/...` - `apps/api/src/services/v1/course.ts` - `apps/api/src/services/v1/audience.ts` Requirements: - follow the route/service/validation split defined in `AGENTS.md` - each route must return a single response type shape - keep business logic in services, not routes - reuse existing DB queries where possible ### 6. OpenAPI and Docs The backend already has OpenAPI infrastructure. The remaining work is to ensure the new public API is included and discoverable. ### OpenAPI requirements - all new `/public-api/v1/*` routes must be documented with the existing Hono OpenAPI patterns - request params, query, body, success, and error responses must be declared - avoid undocumented public API routes ### Spec publishing Use the existing script: - `apps/api/scripts/upload-openapi-spec.ts` Required flow after API changes: 1. Implement or update routes and schemas. 2. Run `pnpm --filter @cio/api upload:openapi`. 3. Verify the latest spec includes the new routes. ### Docs integration Add the public API docs experience in `apps/docs`. Target tasks: 1. Add the API docs page or route. 2. Wire it to the published OpenAPI source. 3. Add docs navigation for API. 4. Verify the docs build still passes. ### 7. Testing Add tests for: - organization automation key auth behavior relevant to public API access - `/v1/courses` - `/v1/audience` - OpenAPI contract inclusion for public API routes Use Hono app-level testing where possible. ### 8. Build and Verification Required verification commands after implementation: 1. `pnpm --filter @cio/api build` 2. `pnpm --filter @cio/api upload:openapi` 3. `pnpm --filter @cio/docs build` Manual verification: 1. `/org/[slug]/automation` still redirects to `/org/[slug]/automation/mcp` 2. `/org/[slug]/automation/api` now renders the real API page rather than the placeholder 3. API key creation, rotation, and revoke flows work for `type=api` 4. `/public-api/v1/*` routes respond under the new base path 5. docs navigation exposes the public API docs entry