# Minds Client Library

> Embed configured Minds in your application, typed Node client for messaging, events, and Builder API capabilities after you set up with the Minds CLI.

Embed Minds in your product, configure with the CLI first, then wire the same Builder API into your Node backend with full TypeScript types.

`@animocabrands/minds-client-lib` is how you **wrap and embed Minds in your application or product**. Most builders start with the [Minds CLI](/docs/get-started/cli), list Minds, inspect detail, monitor cognition, manage circles, browse the Bazaar, then move here when they are ready to ship. The library exposes the same [Builder API](/docs/api) with TypeScript types: messaging, `waitForReply`, SSE events, and the account operations you already ran from the terminal.

Add the package to the **server side** of your project. Your UI, branding, and user flows stay yours; the client handles typed HTTP and streaming to api.build. Complete [Account setup](/docs/get-started/account-setup), validate your Mind with the CLI, then wire the examples below, or hand them to a coding agent once configuration is done.

## Install

Requires **Node 22+**.

```bash
npm install @animocabrands/minds-client-lib
```

## Authentication

Pass your Builder API key for account and messaging calls. The library sends **`X-Api-Key` only** on authenticated requests (≥0.1.2). **`X-Access-Key`** is deprecated, do not use it. Env name remains `MINDS_BUILDER_API_KEY`.

```typescript
import {
  BUILDER_API_KEY_ENV,
  BUILDER_API_KEY_HEADER,
  createMindsClient,
} from "@animocabrands/minds-client-lib";

const builderApiKey = process.env[BUILDER_API_KEY_ENV];
if (!builderApiKey) throw new Error(`$ is not set`);

const client = createMindsClient();
```

## Create a client

```typescript
import  from "@animocabrands/minds-client-lib";

const client = createMindsClient();
```

`createMindsClient()` is **valid** for the public Bazaar catalog only (`client.bazaar.*`). Auth methods throw `MindsApiError` with code `missing_builder_api_key` at **call time** if no key was provided, not at construction.

## Minds List

`listMinds()` returns every Mind on your account (`mindId`, `name`, `model`, `species`, and related fields).

```typescript
const minds = await client.listMinds();
const mindId = minds[0]?.mindId;
```

`humanId` is resolved from your Builder API key automatically. Pass `listMinds()` when you need an explicit override.

## Mind details

`getMind(mindId)` returns full detail - email, wallet address, chain, species, `isEnabled`, and more:

```typescript
const detail = await client.getMind(mindId);
console.log(detail.email, detail.walletAddress, detail.chain);
```

## Cognition balance and usage

Per-Mind cognition usage and balance mirror the CLI commands. Minds consume cognition when they reason, run tools, and carry out autonomous tasks, not only in conversation. Both take a `mindId` (UUID) and an optional `AbortSignal`.

**Cognition** powers a Mind's reasoning, tool use, and autonomous work. Use `getCognitionUsage` and `getCognitionUsageByTool` for usage over time and per tool. Use `getCognitionBalance` for the remaining cognition balance:

```typescript
const mindId = (await client.listMinds())[0]?.mindId;
if (!mindId) throw new Error("No Minds on this account");

const usage = await client.getCognitionUsage(mindId, );
console.log(usage.items); // [, ...]

const byTool = await client.getCognitionUsageByTool(mindId, );
console.log(byTool.summary); // [, ...]
console.log(byTool.timeline); // [, ...]

const balance = await client.getCognitionBalance(mindId);
console.log(balance.cognition);
```

The two usage methods accept different interval values: `getCognitionUsage` accepts `1m`, `5m`, `15m`, `1h`, `1d`, `1w`, `1M`; `getCognitionUsageByTool` accepts `hour`, `day`, `week`, `month` only. Optional `startTime` and `endTime` (ISO date-time) bound the window on both.

## Mind status

Enable or disable a Mind without deleting it:

```typescript
await client.updateMindStatus(mindId, );
await client.updateMindStatus(mindId, );
```

`updateMindStatus` returns the updated `BuilderMind`.

## Circles

A Mind's circle is its set of human collaborators. `getCircle` returns a `CircleMember[]` array directly; `addCircleMembers` and `removeCircleMembers` return a `CircleMutationResult` envelope with per-email outcomes and a best-effort summary:

```typescript
const members = await client.getCircle(mindId);
console.log(members.map((m) => m.email));

const added = await client.addCircleMembers(mindId, {
  emails: ["someone@company.com", "peer@company.com"],
  isActive: true,
});
console.log(added.summary);

const removed = await client.removeCircleMembers(mindId, {
  emails: ["someone@company.com"],
});
console.log(removed.items);
```

Circles accept **human collaborator emails** only, not Mind `@hellominds.ai` addresses for builders. Mind-to-mind circle membership is not supported via this API. `listCirclesForAccount()` is a convenience that calls `listMinds()` then `getCircle()` for each Mind in parallel; use it for account-wide overviews rather than hot paths.

## Bazaar catalog

The public Bazaar catalog is available as `client.bazaar`, no API key required. Use Bazaar for **ID discovery** (`skillId`, `appId`), then equip those IDs with the Mind equip methods below. Route shapes are in the [API reference](/docs/api) Bazaar and Minds tags; the CLI exposes the same catalog via `minds bazaar`.

List items may include `equippedCount`, how many Minds have equipped that item **platform-wide**. That is popularity across the platform — use `listEquippedSkills` / `listEquippedApps` for the per-Mind equipped set.

```typescript
const catalog = createMindsClient().bazaar;

const skills = await catalog.listSkills();
const skill = await catalog.getSkill("skill_abc123");

const apps = await catalog.listApps();
const app = await catalog.getApp("app_xyz789");

const result = await catalog.collectSearchResults({
  scanMax: 200,
  max: 50,
  sort: "equipped",
  fetchPage: (page, pageSize) => catalog.listSkills(),
  getEquippedCount: (s) => s.equippedCount ?? 0,
  getName: (s) => s.name,
  getCreatedAt: (s) => s.createdAt,
});
```

Apps use `appId` and `appName` (not `name`). `collectSearchResults` auto-paginates up to `scanMax`, applies client-side sort/filter, and reports `truncated` when the scanned set exceeds `max`. `sort: "equipped"` orders by `equippedCount` descending (platform popularity), not per-Mind equip status.

## Equip skills and apps

List, equip, or unequip skills and apps on a Mind (requires a Builder API key). Bodies use ``; mutations return `` with `skillId` / `appId`, `isEquipped`, and `changed` (app results may also include `appVersionId` and `version`):

```typescript
const equippedSkills = await client.listEquippedSkills(mindId);
await client.equipSkills(mindId, );
await client.unequipSkills(mindId, );

const equippedApps = await client.listEquippedApps(mindId);
await client.equipApps(mindId, );
await client.unequipApps(mindId, );
```

Discover IDs via `client.bazaar` first when you need catalog browse.

Equipped skills include `source`: `mind` for catalog / Mind-authored skills, `system` for platform skills (e.g. Skill Architect).

## Conversations

Bind a stable **alias** (e.g. `main`) to a Mind before messaging:

```typescript
await client.ensureConversation("main", mindId);
```

`ensureConversation` is idempotent, if the alias already exists for that Mind, the existing conversation is returned.

Lower-level helpers when you manage aliases yourself:

```typescript
const conversations = await client.listConversations();
const conversation = await client.getConversation("main");
await client.createConversation();
```

## Send and history

```typescript
await client.sendMessage();

const rows = await client.getHistory("main", );
const after = rows.at(-1)?.fingerprint;
const newer = await client.getHistory("main", );
```

`getHistory` returns the full human + Mind transcript. Rows use `senderType` (`1` = human, `0` = Mind). The same field appears on SSE events from `subscribeEvents` / `eventsIterator` / `waitForReply`.

`sendMessage` requires `alias` and `messageText`. Optional `attachments` accept **outbound** objects, prefer a public HTTPS `url` with `fileName` and `mimeType`:

```typescript
await client.sendMessage({
  alias: "main",
  messageText: "Summarize this PDF in one sentence.",
  attachments: [
    {
      url: "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf",
      fileName: "dummy.pdf",
      mimeType: "application/pdf",
      extension: "pdf",
    },
  ],
});
```

Image URLs use the same shape (`mimeType: "image/png"`, etc.). Mind **replies** on `getHistory`, `waitForReply`, and SSE events may include **inbound** artifacts, for example:

```ts
// One attachment on a Mind reply (PDF; artifact body truncated)
{
  artifactId: "7388b65d-144a-49ff-a4dc-cb7c23ded982",
  slug: "whale_watchtower_skill_artifact_1_0_5",
  logicalType: "document",
  mimeType: "application/pdf",
  extension: "pdf",
  artifact: "JVBERi0xLjQK...",
}
```

Outbound send uses `content` for inline payloads; inbound replies use `artifact` for the same role. `slug` and `logicalType` are optional strings, treat values as hints, not a fixed enum. See the [API reference](/docs/api) for field details.

`getLatestHistoryFingerprint` returns the fingerprint of the newest message, pass it as `after` on `getHistory` to fetch only newer rows, or as `afterFingerprint` before `waitForReply` so you spot replies that arrived after you sent.

Resolve which Mind an alias last talked to:

```typescript
const mindId = await client.getMindIdForAlias("main");
```

## Wait for a Mind reply

`waitForReply` listens on the live event stream first, then polls history until a reply arrives or the timeout hits.

```typescript
const before = await client.getLatestHistoryFingerprint("main");

await client.sendMessage();

const outcome = await client.waitForReply({
  alias: "main",
  timeoutMs: 180_000,
  afterFingerprint: before,
  sentMessageText: "Summarize our plan.",
});

if (!outcome.timedOut) {
  console.log(outcome.reply.messageText);
}
```

Use `isReplyEvent` from the package when you filter SSE or history rows yourself.

## Live events (SSE)

Subscribe with callbacks:

```typescript
const sub = client.subscribeEvents({
  alias: "main",
  onEvent: (event) => {
    console.log(event.messageText);
  },
  onError: (err) => console.error(err),
});

// later
sub.close();
```

Or consume the async iterator:

```typescript
for await (const event of client.eventsIterator()) {
  console.log(event.fingerprint, event.messageText);
}
```

Pass `signal` on iterator or subscribe options to cancel when your process shuts down.

## Errors

Failed HTTP calls throw `MindsApiError` with `status`, `code`, and `message`. Calling an auth method without `builderApiKey` throws `missing_builder_api_key` at call time. Map 401/403 to a missing or revoked Builder API key; 429 may include retry guidance.

## Methods

| Area          | Methods                                                                                                   |
| ------------- | --------------------------------------------------------------------------------------------------------- |
| Account       | `listMinds`, `getMind`                                                                                    |
| Cognition     | `getCognitionUsage`, `getCognitionUsageByTool`, `getCognitionBalance`                                     |
| Mind status   | `updateMindStatus`                                                                                        |
| Equip         | `listEquippedSkills`, `equipSkills`, `unequipSkills`, `listEquippedApps`, `equipApps`, `unequipApps`      |
| Circles       | `getCircle`, `addCircleMembers`, `removeCircleMembers`, `listCirclesForAccount`                           |
| Bazaar        | `bazaar.listSkills`, `bazaar.getSkill`, `bazaar.listApps`, `bazaar.getApp`, `bazaar.collectSearchResults` |
| Conversations | `createConversation`, `listConversations`, `getConversation`, `ensureConversation`, `getMindIdForAlias`   |
| Messaging     | `sendMessage`, `getHistory`, `getLatestHistoryFingerprint`                                                |
| Replies       | `waitForReply`, `isReplyEvent`, `isReplyHistoryRow`                                                       |
| Events        | `subscribeEvents`, `eventsIterator`, `parseSseChunk`                                                      |

Types (`BuilderMind`, `Conversation`, `MessageRecord`, `MessagingEvent`, `CognitionBalance`, `BazaarSkill`, `BazaarApp`, `EquippedSkill`, `EquippedApp`, `CircleMember`, `CircleMutationResult`, `CognitionUsageResponse`, `CognitionUsageByToolResponse`, `UpdateMindStatusBody`, …) and `parseHumanIdFromBuilderApiKey` are exported from the package entry.
