Client package
@clay/client is the workspace-scoped, Fern-generated TypeScript SDK that the mobile, landing, and
backend-internal apps use to call the Clay API. It is committed to the repository under
packages/client/src/generated and must be regenerated whenever the OpenAPI contract changes.
Purpose
The package exists to keep every client in lock-step with the backend HTTP surface. A single source of truth —apps/backend/openapi/openapi.yaml — feeds the Fern generator, which emits a typed SDK with
endpoints, environments, errors, and a passthrough fetch escape hatch.
| Concern | Owned by @clay/client |
|---|---|
| Endpoint shapes | Typed resource clients (currently health) |
| Environments | ClayApiEnvironment.Default = "https://api.clay.local" |
| Error model | ClayApiError, ClayApiTimeoutError, handleNonStatusCodeError |
| Transport plumbing | Native fetch, retries, timeouts, request logging |
| Escape hatch | ClayApiClient.fetch(...) for endpoints the SDK does not yet expose |
Package layout
Treat
packages/client/src/generated as build output. Edit the OpenAPI source instead; the next
pnpm generate rewrites this folder.Generation pipeline
The pipeline runs frompackages/client. Fern reads the OpenAPI document emitted by the backend,
runs the TypeScript generator, and writes the result into src/generated.
Author the OpenAPI contract
Hono routes in
@clay/backend describe their shape with @hono/zod-openapi. The backend
exports the live OpenAPI document to apps/backend/openapi/openapi.yaml.Check the contract
From
packages/client, run pnpm check to invoke fern check against the OpenAPI document.
Catch typos, missing paths, or invalid schemas before generation.Regenerate the SDK
From
packages/client, run pnpm generate. The script deletes src/generated, then runs fern generate --group ts-sdk --force from inside fern/. The ts-sdk group uses
fernapi/fern-typescript-sdk@3.72.5 with fetchSupport: native and writes to
../src/generated.Exports
@clay/client resolves through ./src/generated/index.{ts,js} and re-exports the surface below.
ClayApiClient
The main client class. Construct with
new ClayApiClient({environment}). Exposes resource
clients as lazy getters and a fetch(input, init, requestOptions) passthrough for endpoints the
SDK has not yet generated.ClayApi
Namespace export from
./api/index.js. Holds the per-resource sub-clients and request/response
types generated from the OpenAPI document.ClayApiEnvironment
Object literal of base URLs. Today only
Default = "https://api.clay.local". The union type
ClayApiEnvironment is the same as Default until a second environment is added.Errors
ClayApiError (status code, body, raw response, cause) and ClayApiTimeoutError (wraps the
underlying cause). Plus handleNonStatusCodeError for advanced error-mapping use.Environments
packages/client/src/generated/environments.ts
Errors
The SDK throws two top-level error types.| Error | When it is thrown |
|---|---|
ClayApiError | The server returned a non-2xx response, or the response body could not be decoded. |
ClayApiTimeoutError | The request exceeded the configured timeoutInSeconds. Carries the underlying cause. |
ClayApiError exposes:
statusCode?: number— the HTTP status if one was returnedbody?: unknown— the parsed response body (or raw string for non-JSON responses)rawResponse— thecore.RawResponsefrom the fetcher layercause?: unknown— the original throwable, when applicable
handleNonStatusCodeError is the SDK’s internal mapper for fetcher failures (non-json,
body-is-null, timeout, unknown) into either ClayApiError or ClayApiTimeoutError. Apps that
build custom error handlers can reuse this to stay aligned with the SDK’s error model.
Auth surface (planned)
Reference material for the broader auth surface lives in the canonical Clay repository atdocs/vendor/better-auth/llms.txt; see Reference materials
for the refresh workflow. The SDK does not yet expose auth resources — they land with the B2C
auth model documented in Auth model.
Consumption pattern
Import the client from@clay/client, construct it once with an environment, and call resource
methods. The current SDK exposes only health, but the pattern scales as new resources ship.
apps/mobile/app/_lib/clay-api.ts
apps/mobile/app/_lib/use-health.ts
Regeneration commands
All commands below run frompackages/client unless noted.
| Task | Command |
|---|---|
| Validate the OpenAPI contract | pnpm check |
| Regenerate the SDK from OpenAPI | pnpm generate |
| Build (typecheck the generated types) | pnpm build |
| Typecheck generated types | pnpm typecheck |
| Remove the generated folder | pnpm clean |
| Format Fern + config files | pnpm format |
| CI-friendly format check | pnpm format:check |
pnpm generate first runs rm -rf src/generated so stale endpoints cannot linger. Always run
pnpm typecheck after generation and before opening a PR.Acceptance criteria
-
packages/client/src/generatedis committed and matches the latestapps/backend/openapi/openapi.yaml. -
pnpm generateruns cleanly with no generator warnings against the current OpenAPI. -
pnpm typecheck(tsc --noEmit) passes againstsrc/generated/index.d.ts. -
ClayApiEnvironment.Defaultis the only base URL exposed until a second environment is added. - Consumers (
apps/mobile,apps/landing) import from@clay/client, never from a hand-written wrapper that re-exports the same types.
Related
Architecture
How the generated client fits the four-app Turborepo surface.
Workspace
The pnpm + Turborepo layout and per-package scripts that ship
@clay/client.API reference
The implemented HTTP routes that the SDK’s resource clients wrap.
OpenAPI pipeline
The backend OpenAPI export pipeline that feeds Fern generation.

