# Admin API SDK

> Drive the FirstByte console's entire administration surface from your own code — one org-scoped API key, end-to-end types.

`@clutchcall/admin-sdk` is a typed TypeScript client for the same administration
API the FirstByte console runs on. Everything you can do in the console —
agents, trunks, call records, recordings, analytics, webhooks — is callable,
with input and output types generated from the live server surface. Build your
own dashboard or backoffice on it; the console itself consumes this exact
package, so the contract cannot drift.

## Install

```sh
npm install https://artifacts.clutchcall.dev/npm/tarballs/clutchcall-admin-sdk-0.1.0.tgz @trpc/client
```

(The artifacts origin is shared across brands — the same origin that serves the
[prebuilt binaries](/sdks/prebuilt-binaries).)

## Authenticate

Mint an MCP access key in the portal under **Settings → API Keys**. Choose the
narrowest scopes your integration needs. A key:

* acts as the user who minted it, never exceeding what that user can do right
  now — demote or remove them and the key shrinks or stops with them;
* is pinned to one organization — naming another org is refused, not obeyed;
* carries narrow `mcp:<domain>:<action>` scopes — a key minted to read call
  records cannot create a trunk.

```ts
import { createAdminClient } from '@clutchcall/admin-sdk';

const admin = createAdminClient({
  baseUrl: 'https://portal.firstbyte.fast',
  apiKey: process.env.CLUTCHCALL_API_KEY!,   // mpk_…
  orgId: 'your-org-id',
});
```

## Call history, recordings, analytics

```ts
// Call history — start / end / duration / status per call, newest first.
const { rows, total } = await admin.cdr.list.query({
  orgId, limit: 50, fromMs: Date.now() - 86_400_000,
});

// Recordings — every call with a stored recording…
const recs = await admin.cdr.recordings.query({ orgId, limit: 50 });

// …and a 1-hour presigned download URL for one:
const { url } = await admin.storage.signedUrl.query({
  orgId, callSid: rows[0].call_id,
});
```

For **live** call lifecycle events (ringing, connected, ended) subscribe to
[webhooks](/modalities/voice/api/webhooks) instead of polling — the CDR surface is the
historical record, webhooks are the real-time feed.

## Voice agents

```ts
// Set the DEFAULT context metadata your external agent receives as
// ctx.job.metadata (LiveKit transport plugin — see the LiveKit integration
// page). One call can override a key with telephony.originate's `metadata`.
// For labels the platform must understand — environment, team, cost centre —
// use tags instead. Tags are inherited and queryable; metadata is neither.
await admin.adminAgents.updateConfig.mutate({
  orgId, agentId,
  patch: { metadata: { crm_pipeline: 'inbound-2026', script_rev: '7' } },
});
```

## Notes

* Operations take `orgId` in their input. The client fills it in at runtime
  when omitted, but the types ask for it — pass it explicitly.
* Errors are `TRPCClientError`. `FORBIDDEN` naming a scope means the key is
  real but was minted without that authority — mint a purpose-specific key
  rather than widening an existing one.
* The account routes (sessions, passwords, identities) are excluded from the
  key-reachable surface by construction.
