npm package: @masheev/client
Description: Masheev API client — tRPC + Better Auth for React, TanStack, and server
Author: Masheev
Latest version: 0.1.0

<!-- AUTO-GENERATED from apps/docs/content — do not edit directly. -->
<!-- Run `pnpm docs:sync` after editing the source in apps/docs/content/ -->

# @masheev/client

Type-safe API client for Masheev. Wraps tRPC and Better Auth with sub-path exports for React, server-side, and TanStack Start environments.

## Installation

```bash
pnpm add @masheev/client
```

## Entry Points

| Import path | Use case |
|---|---|
| `@masheev/client` | Vanilla TS/JS — direct `query()` / `mutate()` calls |
| `@masheev/client/react` | React — TanStack Query hooks (`useQuery`, `useMutation`) |
| `@masheev/client/server` | Server-side — API token / API key auth |
| `@masheev/client/tanstack` | TanStack Start — automatic cookie forwarding |
| `@masheev/client/types` | Type-only — `Organization`, `User`, `Session`, etc. |

## Quick Start

### React

```tsx
import { apiClient, authClient } from "@masheev/client/react";

function InboxList() {
  const { data: session } = authClient.useSession();

  const { data, isLoading } = apiClient.inboxes.list.useQuery(
    { orgId: session?.user.activeOrganizationId! },
    { enabled: !!session },
  );

  if (isLoading) return <p>Loading...</p>;

  return (
    <ul>
      {data?.inboxes.map((inbox) => (
        <li key={inbox.id}>{inbox.name}</li>
      ))}
    </ul>
  );
}
```

### Vanilla

```ts
import { apiClient, authClient } from "@masheev/client";

const session = await authClient.getSession();

const { inboxes } = await apiClient.inboxes.list.query({
  orgId: "org_123",
});
```

### Server-side

```ts
import { serverApi, createServerApiClients } from "@masheev/client/server";

// Pre-configured (reads TEMPLATE_API_TOKEN / TEMPLATE_API_KEY env vars)
const { inboxes } = await serverApi.inboxes.list.query({ orgId: "org_123" });

// Or create a custom client
const { apiClient } = createServerApiClients({
  baseURL: "https://api.masheev.com",
  apiToken: process.env.MY_API_TOKEN,
});
```

---

## Authentication (`authClient`)

### Session

```ts
authClient.getSession()           // Promise — current session
authClient.useSession()           // React hook — { data, isPending }
```

### Sign In / Sign Up

```ts
authClient.signIn.email({ email, password })
authClient.signUp.email({ email, password, name? })
authClient.magicLink.sendEmail({ email, callbackURL? })
authClient.signIn.oauth(provider, options?)
authClient.signOut()
```

### Password Management

```ts
authClient.changePassword({ currentPassword, newPassword })
authClient.resetPassword({ email, redirectURL })
authClient.updatePassword({ token, newPassword })
```

### Two-Factor Authentication

```ts
authClient.twoFactor.enable({ password })       // returns secret + backup codes
authClient.twoFactor.disable({ password })
authClient.twoFactor.getTotpUri()
authClient.twoFactor.generateBackupCodes({ password })
authClient.twoFactor.verifyTotp({ code })
```

### Organization Management

```ts
authClient.organization.create({ name, slug? })
authClient.organization.list()
authClient.organization.setActive({ organizationId })
authClient.organization.update({ organizationId, name?, slug? })
authClient.organization.delete({ organizationId })
authClient.organization.getFullOrganization({ organizationId })

// React hooks
authClient.useListOrganizations()
authClient.useActiveOrganization()
authClient.useActiveMember()
authClient.useActiveMemberRole()
```

### Members & Invitations

```ts
authClient.organization.inviteMember({ organizationId, email, role? })
authClient.organization.acceptInvitation({ invitationId })
authClient.organization.cancelInvitation({ invitationId })
```

### Teams

```ts
authClient.organization.createTeam({ organizationId, name })
authClient.organization.listTeams({ organizationId })
authClient.organization.updateTeam({ organizationId, teamId, name? })
authClient.organization.deleteTeam({ organizationId, teamId })
authClient.organization.addTeamMember({ organizationId, teamId, memberId, role? })
authClient.organization.removeTeamMember({ organizationId, teamId, memberId })
```

### API Keys

```ts
authClient.apiKey.create({ name? })
authClient.apiKey.list()
authClient.apiKey.get({ keyId })
authClient.apiKey.delete({ keyId })
```

### User Profile

```ts
authClient.updateUser({ name?, image? })
authClient.deleteUser()
authClient.changeEmail({ newEmail, password })
authClient.sendVerificationEmail({ email })
```

---

## tRPC Routers (`apiClient`)

Every router supports type-safe inputs and outputs. In React, replace `.query()` with `.useQuery()` and `.mutate()` with `.useMutation()`.

### Inboxes

```ts
apiClient.inboxes.list.query({ orgId, channel? })
apiClient.inboxes.get.query({ id })
apiClient.inboxes.create.mutate({ orgId, name, channel, ... })
apiClient.inboxes.update.mutate({ id, name?, ... })
apiClient.inboxes.delete.mutate({ id })
```

### Contacts

```ts
apiClient.contacts.list.query({ orgId, search?, limit?, cursor? })
apiClient.contacts.get.query({ id })
apiClient.contacts.find.query({ orgId, email?, phone?, externalId? })
apiClient.contacts.create.mutate({ orgId, name?, email?, phone?, ... })
apiClient.contacts.update.mutate({ id, name?, ... })
apiClient.contacts.delete.mutate({ id })
apiClient.contacts.merge.mutate({ orgId, winnerId, loserId })
apiClient.contacts.export.query({ id })          // GDPR data export
apiClient.contacts.anonymize.mutate({ id })      // GDPR erasure
```

### Conversations

```ts
apiClient.conversations.list.query({ orgId, status?, inboxId?, assigneeId?, ... })
apiClient.conversations.get.query({ id })
apiClient.conversations.create.mutate({ orgId, inboxId, contactId, ... })
apiClient.conversations.update.mutate({ id, version, ... })   // optimistic locking
apiClient.conversations.assign.mutate({ id, assigneeId? })
apiClient.conversations.resolve.mutate({ id, outcome? })
apiClient.conversations.snooze.mutate({ id, until })
apiClient.conversations.delete.mutate({ id })
apiClient.conversations.batchUpdate.mutate({ ids, action, ... })
```

### Messages

```ts
apiClient.messages.list.query({ conversationId, cursor?, limit? })
apiClient.messages.get.query({ id })
apiClient.messages.create.mutate({ conversationId, content, channel?, ... })
apiClient.messages.updateStatus.mutate({ id, status })
apiClient.messages.markRead.mutate({ conversationId })
apiClient.messages.delete.mutate({ id })
```

### Webhooks

```ts
apiClient.webhooks.eventTypes.query({})
apiClient.webhooks.list.query({ orgId })
apiClient.webhooks.get.query({ id, orgId })
apiClient.webhooks.create.mutate({ orgId, url, events, name?, ... })
apiClient.webhooks.update.mutate({ id, orgId, status?, ... })
apiClient.webhooks.delete.mutate({ id, orgId })
apiClient.webhooks.test.mutate({ id, orgId })
apiClient.webhooks.regenerateSecret.mutate({ id, orgId })
```

### AI Agents

```ts
apiClient.aiAgents.list.query({ orgId, status? })
apiClient.aiAgents.get.query({ orgId, agentId })
apiClient.aiAgents.create.mutate({ orgId, name, systemPrompt, ... })
apiClient.aiAgents.update.mutate({ orgId, agentId, ... })
apiClient.aiAgents.delete.mutate({ orgId, agentId })
```

### Voice Agents

```ts
apiClient.voiceAgents.list.query({ orgId })
apiClient.voiceAgents.get.query({ orgId, agentId })
apiClient.voiceAgents.create.mutate({ orgId, ... })
apiClient.voiceAgents.update.mutate({ orgId, agentId, ... })
apiClient.voiceAgents.sync.mutate({ orgId, agentId })
apiClient.voiceAgents.delete.mutate({ orgId, agentId })
```

### Knowledge

```ts
apiClient.knowledge.list.query({ orgId })
apiClient.knowledge.get.query({ id })
apiClient.knowledge.create.mutate({ orgId, ... })
apiClient.knowledge.update.mutate({ id, ... })
apiClient.knowledge.delete.mutate({ id })
apiClient.knowledge.stats.query({ orgId })
```

### Labels

```ts
apiClient.labels.list.query({ orgId })
apiClient.labels.get.query({ id })
apiClient.labels.create.mutate({ orgId, name, color? })
apiClient.labels.update.mutate({ id, name?, color? })
apiClient.labels.delete.mutate({ id })
```

### Notes

```ts
apiClient.notes.list.query({ conversationId })
apiClient.notes.get.query({ id })
apiClient.notes.create.mutate({ conversationId, content })
apiClient.notes.update.mutate({ id, content })
apiClient.notes.delete.mutate({ id })
```

### Escalations

```ts
apiClient.escalations.list.query({ orgId, ... })
apiClient.escalations.get.query({ id })
apiClient.escalations.create.mutate({ orgId, conversationId, reason?, ... })
apiClient.escalations.assign.mutate({ id, userId })
apiClient.escalations.resolve.mutate({ id })
apiClient.escalations.dismiss.mutate({ id })
apiClient.escalations.stats.query({ orgId })
```

### Canned Responses

```ts
apiClient.cannedResponses.list.query({ orgId })
apiClient.cannedResponses.get.query({ id })
apiClient.cannedResponses.create.mutate({ orgId, title, content })
apiClient.cannedResponses.update.mutate({ id, title?, content? })
apiClient.cannedResponses.delete.mutate({ id })
```

### Phone Numbers

```ts
apiClient.phoneNumbers.list.query({ orgId })
apiClient.phoneNumbers.get.query({ id })
apiClient.phoneNumbers.create.mutate({ orgId, ... })
apiClient.phoneNumbers.update.mutate({ id, ... })
apiClient.phoneNumbers.delete.mutate({ id })
```

### Other Routers

```ts
apiClient.billing.getBalance.query({ orgId })
apiClient.billing.getPlan.query({ orgId })
apiClient.auditLogs.list.query({ orgId, ... })
apiClient.voices.list.query({ orgId })
apiClient.org.panelStats.query({ orgId })
apiClient.files.*                               // file upload/download
apiClient.chat.*                                // chat operations
apiClient.realtime.*                            // real-time subscriptions
apiClient.admin.organizations.list.query(...)   // system admin
apiClient.admin.stats.getOverview.query({})     // system admin
```

---

## Types

```ts
import type {
  Organization,
  Member,
  Team,
  Invitation,
  User,
  Session,
  ApiKey,
  OrganizationWithParsedMeta,
  TeamWithParsedMeta,
} from "@masheev/client/types";
```

---

## Error Handling

tRPC errors are thrown as `TRPCClientError` with standard codes:

| Code | Meaning |
|---|---|
| `UNAUTHORIZED` | Missing or invalid session |
| `FORBIDDEN` | Insufficient permissions |
| `NOT_FOUND` | Resource does not exist |
| `BAD_REQUEST` | Invalid input |
| `CONFLICT` | Optimistic locking version mismatch |

```ts
import { TRPCClientError } from "@trpc/client";

try {
  await apiClient.inboxes.get.query({ id: "bad" });
} catch (err) {
  if (err instanceof TRPCClientError) {
    console.error(err.message, err.data?.code);
  }
}
```

---

## Configuration

The client reads its base URL from `@masheev/shared/config`:

| Environment | API URL | tRPC URL |
|---|---|---|
| Development | `http://localhost:3015` | `http://localhost:3015/api` |
| Production | `https://api.masheev.com` | `https://api.masheev.com/api` |

Server clients accept an explicit `baseURL` override via `createServerApiClients()`.