---
title: TypeScript SDK
description: Open a Croche project and run a first conversation from Node.
---

[!nav:main](/nav/main.md)

Use the TypeScript SDK to connect your app's conversations and actions to Croche.

> **0.5.0 package release pending verification.** The install command below targets the planned public release. The example targets the updated `debugKey` API and automatic connection setup; compatibility and a live first turn still need verification against the published 0.5.0 package.

## Install

Use **Node.js 22+ on macOS/Linux** for this quickstart. **Windows verification pending**.

```sh
npm install @h1r-ai/croche@0.5.0
```

You do not need a Rust toolchain to use the SDK.

## Selected Croche.open options

| Option | What your app supplies |
| --- | --- |
| `projectKey` | The public project key returned by CLI project creation |
| `debugKey` | A secret debug key for local testing; never ship it |
| `serverKey` | A production backend credential; never send it to clients |
| `user` | The authenticated user's stable ID |
| `storage` | Your storage adapter, such as `FileStorage` on Node |
| `config` | App instructions, agents, and tool declarations |
| `config.model` | The app's model choice. Set it explicitly; see [Models](../models.md) for recommendations, allowed IDs and default/fallback behavior. |
| `handlers` | App handlers keyed by tool name |
| `language` | The **user's language**, as a BCP-47 tag. Selects `config.languages.<tag>` by exact tag first, then primary subtag; otherwise uses the base config as is. In a browser, omission uses `navigator.language`. For a Node backend, pass the user's language explicitly. |
| `config.defaultLanguage` | The base config's authored language (default `"en"`), used only for config-layer selection; **not** the user's language or a fallback for it |
| `config.languages.<tag>` | Translated config for a matching user-language tag; no match leaves the base config unchanged |

An English-only config can serve a Korean user. The model is not told `language`; it replies in the language the user writes unless instructions say otherwise. See [app config and language](../concepts/config.md).

## Run a first turn

Follow the [quickstart](../quickstart.md): after the agent installs the CLI, your only credential setup is user-run `croche login` in your private terminal, with the developer key entered at its hidden prompt. Login remains pending release verification. Your coding agent creates the project and uses the **planned** `croche debug-keys create --project PROJECT_ID` command to write `CROCHE_DEBUG_KEY` into `.env` without displaying the secret. The command and file protections remain pending release verification.

The agent fills in the public project key below from project creation. It must not open or print the secret file in chat. `CROCHE_DEVELOPER_KEY` is an account credential, never an SDK option or app-bundle value.

```ts
import { Croche } from '@h1r-ai/croche';
import { FileStorage } from '@h1r-ai/croche/node';

function required(name: string): string {
  const value = process.env[name];
  if (!value) throw new Error(`Load the private CLI-created env file containing ${name}.`);
  return value;
}

const croche = await Croche.open({
  language: 'en', // The demo user's language, not the config's authored language.
  projectKey: '<public project key returned by CLI>',
  debugKey: required('CROCHE_DEBUG_KEY'),
  user: 'local-demo-user',
  storage: new FileStorage('./croche-data/local-demo-user'),
  config: {
    model: 'deepseek/deepseek-v4.1-flash',
    instructions: 'You are a helpful assistant. Keep your replies brief.',
  },
  handlers: {},
});

try {
  const agent = await croche.agent();
  const result = await agent.request({
    id: crypto.randomUUID(),
    text: 'Hello! What can you help me with?',
  });

  const reply = result.messages.at(-1);
  if (result.status !== 'answered' || reply?.status !== 'completed' || !reply.text?.trim()) {
    console.error('First turn did not complete:', result.status, reply?.error?.code ?? 'no_reply');
    process.exitCode = 1;
  } else {
    console.log(reply.text);
  }
} finally {
  await croche.close();
}
```

Use your existing TypeScript build workflow. After compiling this example to JavaScript, the agent can run the output with `node --env-file=.env PATH_TO_OUTPUT.js`; the [quickstart](../quickstart.md) also has a directly runnable `.mjs` example. Plain Node does not load `.env` automatically. `--env-file` (Node 20.6+, within Croche's Node 22+ requirement) loads the private file without printing it; the project's existing dotenv setup is an alternative if it loads before `Croche.open`. CLI login alone does not supply this SDK value. No additional environment-loader package is required for the Node example. Windows execution remains unverified.

Use your user's BCP-47 language tag instead of the demo's `'en'`. `local-demo-user` is a single-user demo identity. In a real app, use the authenticated user's stable ID and keep each user's storage separate. Do not trust a user ID supplied by an unauthenticated client.

## Messages and streaming

Use `request(...)` when you want to await a reply. Use `send(...)` when you want to queue an input and render its events through a subscription.

```ts
const subscription = agent.subscribe({
  onEvent: (event) => {
    if (event.type === 'message.delta') {
      console.log('Partial:', event.text);
    }
    if (event.type === 'message.end') {
      console.log('Final:', event.text);
    }
  },
});

await agent.send({
  id: crypto.randomUUID(),
  text: 'Tell me something useful.',
});

// Keep the subscription for the lifetime of your conversation UI.
// When that UI is disposed:
// subscription.close();
```

This fragment assumes an open `agent` from the preceding example, before the project is closed. Partial text is provisional; use the final event for the completed message. See [messages and activity](../concepts/messages.md).

## Browser apps and production

Never put a debug key or server key in browser code, including local development. Run `Croche.open` on your Node backend and let the browser call your app endpoint. Load the private `.env` only into the server process with `--env-file` or existing server-side dotenv; never use frontend `VITE_` or `NEXT_PUBLIC_` variables for secrets.

Use a **server key** from Studio as `serverKey` for that production backend, rather than a development `debugKey`. Keep server keys on the server. Never send them to browser code or a mobile app.

For mobile apps, see [React Native](react-native.md), [iOS](ios.md), or [Android](android.md).
