--- alwaysApply: true --- # SDK Generation Pipeline Zod Schemas → ORPC Contracts → OpenAPI Spec → Speakeasy SDKs → autumn-js Wrapper → React Hooks Webhook Zod Schemas → Webhook Registry → OpenAPI Spec (injected) + Svix Event Types Run `bun api` from root to execute the full pipeline. --- ## 1. Zod Schema Layer **Location:** `shared/api/` - Use `.describe()` for field descriptions in API docs - Use `.meta({ internal: true })` for fields stripped from public OpenAPI spec - Use `.meta({ id: "SchemaName" })` for schema registration --- ## 2. ORPC Contract Layer **Location:** `packages/openapi/v2.1/contracts/` Each contract defines: `method`, `path`, `operationId`, `tags`, `input`, `output`. All contracts aggregated in `packages/openapi/v2.1/contracts/index.ts`. --- ## 3. OpenAPI Generation **Entry point:** `packages/openapi/v2.1/openapi2.1.ts` Steps: Register internal schemas → Generate via ORPC → Apply Speakeasy settings → Inject global headers → Remove internal fields. The generated SDK/OpenAPI `x-api-version` default must come from `LATEST_VERSION` in `shared/api/versionUtils/ApiVersion.ts` and currently resolves to `2.2.0`. --- ## 4. SDK Generation **Entry point:** `packages/openapi/api.ts` - **TypeScript:** `bunx speakeasy run -t autumn` → `packages/sdk/` - **Python:** `bunx speakeasy run -t autumn-python` → `others/python-sdk/` --- ## 5. autumn-js Architecture **Location:** `packages/autumn-js/src/` ### Directory Structure ``` packages/autumn-js/src/ ├── backend/ # Server-side │ ├── adapters/ # Framework adapters (hono.ts, next.ts) │ └── core/ │ ├── handlers/ # Request processing │ │ ├── coreHandler.ts # Main handler factory │ │ ├── executeRoute.ts # Route execution logic │ │ └── resolveIdentity.ts │ ├── routes/ │ │ ├── defaultRoutes.ts # Route definitions │ │ └── routeBuilder.ts # rou3-based router │ ├── types/ # Type definitions │ └── utils/ # backendRes, secretKeyCheck ├── react/ # Client-side │ ├── client/ │ │ ├── AutumnClient.ts # Client factory │ │ └── internal/httpClient.ts │ ├── hooks/ # useCustomer, useListPlans │ └── AutumnContext.tsx # React context provider ├── sdk/ # Re-exports from @useautumn/sdk └── utils/ # Shared utilities ``` ### Backend: Route Definitions Routes defined in `defaultRoutes.ts` using `RouteDefinition`: ```typescript type RouteDefinition = { route: string; // RPC-style name: "customers.get_or_create" sdkMethod: (autumn, args) => Promise; inject?: { customerId?: boolean; customerData?: boolean }; customHandler?: CustomHandlerFn; // For special logic requireCustomer?: boolean; // Default: true }; ``` Standard routes use `inject` to auto-add identity fields. Use `customHandler` for complex logic (e.g., `customers.get_or_create` handles `errorOnNotFound`). ### Backend: Framework Adapters Adapters in `backend/adapters/` convert framework requests to `UnifiedRequest`: ```typescript type UnifiedRequest = { method: string; path: string; body: unknown; raw: unknown; // Original request for identify() }; ``` ### React Client `AutumnClient.ts` creates a client with namespaced methods that call backend routes: ```typescript const client = createAutumnClient({ baseUrl: "/api/autumn" }); // client.customers.getOrCreate(), client.billing.attach(), client.plans.list() ``` ### React Hooks Hooks use TanStack Query with consistent patterns: ```typescript export const useCustomer = (params: UseCustomerParams = {}) => { const client = useAutumnClient(); const { errorOnNotFound, queryOptions, ...sdkParams } = params; return useQuery({ queryKey: ["autumn", "customer", sdkParams], queryFn: async () => { const response = await client.customers.getOrCreate({...}); if (response.error) throw response.error; return response.data; }, ...queryOptions, }); }; ``` ### SDK Casing Convention **Important:** The TypeScript SDK and React hooks use **camelCase** for all properties. Speakeasy automatically transforms snake_case API responses to camelCase. - API response: `{ plan_id: "pro", created_at: 123, auto_enable: true }` - SDK/Hook response: `{ planId: "pro", createdAt: 123, autoEnable: true }` When writing documentation examples for React hooks or the TypeScript SDK, always use camelCase: - `data?.balances?.messages?.featureId` (not `feature_id`) - `subscription.planId` (not `plan_id`) - `plan.createdAt` (not `created_at`) --- ## 6. Webhook Pipeline **Registry:** `shared/api/webhooks/webhookRegistry.ts` Each webhook is defined as a `WebhookDefinition` with `eventType`, `operationId`, `title`, `schema` (Zod), `description`, and `group`. Schemas live in `shared/api/webhooks/`. ### OpenAPI injection `packages/openapi/v2.1/webhooks/injectWebhooks.ts` reads the registry and injects `webhooks` entries into the generated OpenAPI document. Only definitions with a Zod schema are included. ### Svix sync `packages/openapi/scripts/svixPush.ts` pushes event types to Svix (create or update). It does **not** delete event types missing from the registry. ### Adding a new webhook event 1. **Create schema** in `shared/api/webhooks/` (Zod schema with `.meta()` descriptions) 2. **Add to registry** in `shared/api/webhooks/webhookRegistry.ts` 3. **Add to `WebhookEventType`** enum in `shared/api/webhooks/webhookEventType.ts` 4. **Re-export** from `shared/api/webhooks/index.ts` 5. **Regenerate** via `bun api` (webhooks are injected into the OpenAPI spec automatically) 6. **Push to Svix** via `bun run --cwd packages/openapi scripts/svixPush.ts` --- ## 7. Testing (sdk-test) **Location:** `apps/sdk-test/` Scenario pages organized by provider: `scenarios/core/`, `scenarios/better-auth/`, `scenarios/convex/`. --- ## Adding a New API Route 1. **Add contract** in `packages/openapi/v2.1/contracts/` 2. **Add to router** in `packages/openapi/v2.1/contracts/index.ts` 3. **Regenerate** via `bun api` 4. **Add route definition** in `defaultRoutes.ts` 5. **Add client method** in `AutumnClient.ts` 6. **Add hook** in `react/hooks/` if needed 7. **Add scenario test** in `apps/sdk-test/` --- ## Key File Paths | Purpose | Path | |---------|------| | Zod schemas | `shared/api/` | | ORPC contracts | `packages/openapi/v2.1/contracts/` | | Generated TS SDK | `packages/sdk/` | | Backend core handler | `packages/autumn-js/src/backend/core/handlers/coreHandler.ts` | | Route definitions | `packages/autumn-js/src/backend/core/routes/defaultRoutes.ts` | | React client | `packages/autumn-js/src/react/client/AutumnClient.ts` | | React hooks | `packages/autumn-js/src/react/hooks/` | | SDK tests | `apps/sdk-test/` | | Webhook schemas | `shared/api/webhooks/` | | Webhook registry | `shared/api/webhooks/webhookRegistry.ts` | | Webhook OpenAPI injection | `packages/openapi/v2.1/webhooks/injectWebhooks.ts` | | Svix push script | `packages/openapi/scripts/svixPush.ts` |