SDKs
One contract, one spec, many languages. The TypeScript SDK ships today. Python and Swift land when the first user asks. Until then, the OpenAPI spec at /openapi.json lets anyone generate a client in…
One contract, one spec, many languages. The TypeScript SDK ships today. Python and Swift land when the first user asks. Until then, the OpenAPI spec at
/openapi.jsonlets anyone generate a client in any language in minutes.
Which SDK exists today
| Language | Status | Package | Reference |
|---|---|---|---|
| TypeScript | Stable | @amy/sdk | Reference |
| Python | Planned | amy-sdk (PyPI) | When the first Python user asks |
| Swift | Planned | amy-sdk (SPM) | When the native iOS app starts |
The CLI you run today is itself a TypeScript SDK client, every
amy ... command is a thin wrapper around an SDK method. If the SDK
can't do it, the CLI can't either.
Why TypeScript first
Three reasons, in order:
- It's the canonical client. The Worker is written in TypeScript
with Hono. Hono's
hc()produces a typed fetch client from the route definitions, free end-to-end types with no extra build step. Every other language SDK is downstream of this one. - Mobile uses it too. The React Native app talks to Amy with the same package, no JS / TS split, no second implementation to keep in sync.
- It's the SDK the team uses. The CLI, the e2e tests, the
internal tooling all consume
@amy/sdk. If the TS SDK is broken we notice immediately; the contract for every other language is downstream.
When someone asks for a Python SDK we'll ship one. Until then we'd rather spend that build effort on the contract that drives all of them.
How SDKs are generated
The pipeline has one source of truth: the Zod schemas in
packages/contracts/.
Zod schemas the source of truth
│
▼ @hono/zod-openapi
/openapi.json served live by the Worker
│
├──── hc() → TypeScript SDK (free with Hono, zero codegen)
├──── Fern → Python SDK (when asked)
└──── Fern → Swift SDK (when the iOS app starts)The same /openapi.json your Worker serves is what we'd feed into
any code generator. We use Fern's free
tier for non-TS languages because it produces idiomatic, well-typed
clients without us writing a generator from scratch.
You don't have to wait for us. The spec is always live at
/openapi.json on any deployed Amy Worker, point your favorite
generator at it and ship.
# Generate your own client in any language Fern supports
fern generate --openapi https://amy.heyamy.xyz/openapi.json
# Or with the OpenAPI generator
openapi-generator-cli generate \
-i https://amy.heyamy.xyz/openapi.json \
-g <lang> -o ./my-amy-sdkCommon targets that work out of the box: Python, Go, Rust, Java, Kotlin, Swift, C#, Ruby, PHP. The spec is OpenAPI 3.1, which most modern generators handle natively.
What every SDK ships with
Whatever language we generate next, these are non-negotiable:
| Feature | Why |
|---|---|
| Fully typed methods (matching the Zod contract) | The whole point of the schema-first design |
Automatic retries with exponential backoff for 429 / 5xx | Network is unreliable; clients shouldn't have to handle it |
Auto-generated Idempotency-Key (UUIDv4) on writes | Safe to retry without duplicating side effects |
| Async iterator / stream interface for SSE endpoints | "Watch Amy think" UX works the same everywhere |
Typed error classes with stable code fields | if err is NotFoundError works in every language |
| Cursor-based pagination helpers | One for loop walks every page |
See TypeScript reference for the concrete shapes; other SDKs will mirror them.
Stability
We follow semver. The TypeScript SDK is at 0.x, breaking
changes can happen between minor versions during the beta. Once we
hit 1.0, the rules tighten:
| Change | Counts as breaking? |
|---|---|
| Removing or renaming an endpoint | Yes |
| Removing a field from a response | Yes |
| Changing a field's type or making it nullable | Yes |
| Removing or renaming an SDK method | Yes |
| Changing a method's positional argument order | Yes |
| Adding a new endpoint | No |
| Adding an optional request field | No |
| Adding a new response field | No |
| Adding a new SDK method | No |
| Adding a new event type to a stream | No |
Clients should always ignore unknown fields in responses and unknown event types in streams. Both are how we ship non-breaking additions.
For API versioning specifically: every endpoint lives under /v1/.
When we ship /v2/, both run side by side and /v1/ stays
available for at least 6 months with deprecation headers. The
TypeScript SDK pins to a single API version per release; bumping
your SDK is how you opt in to a new API version.
Where to next
- TypeScript SDK, install, initialize, every method.
- API reference, the raw endpoints behind the SDK.
- Architecture, why the contract is the source of truth.
- Getting started, your first SDK call against a live backend.
Add a new wearable adapter
Goal: make Amy understand a wearable it doesn't already speak to. Two paths: either Terra already supports it (config-only) or it doesn't (a small custom adapter conforming to Amy's normalizer contra…
TypeScript
The TypeScript SDK is the canonical client. It works in Bun, Node, Deno, browsers, and React Native. Every other language SDK is generated from the same OpenAPI spec.