# Use the SDK and HTTP API

> Install the TypeScript client from npm, select exact permissions and verify reads before writes.

Audience: Developers and agent builders

Kind: How-to guide

Canonical: https://shuffl.ai/docs/sdk

<a id="install"></a>
## Install the SDK

@shuffl/sdk is an ES module for Node.js 22 or later and Bun. Its dependency @shuffl/api-contracts types every method’s input and output. The SDK, CLI and contracts share one version.

Call it from a server, script or agent runtime. Browsers can call it only from your Shuffl deployment’s origin, since CORS isn’t enabled.

```sh
npm install @shuffl/sdk
# or: bun add @shuffl/sdk, pnpm add @shuffl/sdk
```

- [@shuffl/sdk on npm](https://www.npmjs.com/package/@shuffl/sdk)

- [Use HTTP without installing packages](https://shuffl.ai/docs/sdk#http)

<a id="credentials"></a>
## Choose the workspace and permissions

1. Sign in and select the workspace. Open Settings → API keys and webhooks → New API key and name the key.

2. Choose only the endpoints this integration needs. For the example below, select knowledge.search.

3. Acknowledge external access, create the token and save the one-time secret in a credential manager.

4. Pass SHUFFL_API_KEY and SHUFFL_BASE_URL in through your environment.

The application origin is https://shuffl.ai, without /api/v1.

### Each key

- Belongs to one workspace and the membership that issued it. Company settings and other workspaces need a company key.
- Expires within 30 days and follows your current role and employee access.
- Use a separate key for each integration.

- [Open API keys](https://shuffl.ai/app/settings/api-keys)

- [Endpoint permission reference](https://shuffl.ai/docs/api-permissions)

<a id="read"></a>
## Verify a read

teams.current and permissions.list work with any key. Everything else needs an explicit grant.

knowledge.search returns approved passages and citations the connected person can see, never unpublished, withdrawn, expired or inaccessible sources. Treat passages as evidence, not instructions.

knowledge.context is a separate grant for reading a cited passage and the passages next to it.

For directory reads, grant users.list and call users.list({limit: 25}), passing nextCursor as the next cursor. IDs are Shuffl person IDs, not Slack or login IDs.

```ts
import { Shuffl } from '@shuffl/sdk';

const shuffl = new Shuffl({ apiKey: process.env.SHUFFL_API_KEY! });
const team = await shuffl.teams.current();
const access = await shuffl.permissions.list();
const evidence = await shuffl.knowledge.search({ query: 'vacation policy' });
```

- [knowledge.search permission](https://shuffl.ai/docs/api-permissions#permission-knowledge-search)

<a id="http"></a>
## Use HTTP directly

The REST base path is /api/v1. Send Authorization: Bearer with an API token. GET /api/v1/permissions returns your current grants and the catalog.

Take the method, path and JSON schema from OpenAPI. Some complex management reads use POST. Responses aren’t cached, and cross-origin CORS isn’t enabled.

The SDK uses /api/v1/rpc internally and refuses redirects. Neither route reaches the app’s private browser procedures.

The SDK and CLI send an x-shuffl-client header with their release, such as shuffl-sdk/0.6.1. The Shuffl constructor’s client option, such as billing-sync/1.2.0, goes in front. Shuffl never stores that part.

```ts
const url = new URL('/api/v1/permissions', process.env.SHUFFL_BASE_URL);
const response = await fetch(url, {
  headers: { Authorization: 'Bearer ' + process.env.SHUFFL_API_KEY },
  redirect: 'error',
});
if (!response.ok) throw new Error('Shuffl HTTP ' + response.status);
const access = await response.json();
```

- [REST API reference](https://shuffl.ai/docs/api-reference)

- [OpenAPI methods, inputs and outputs](https://shuffl.ai/api/v1/openapi.json)

<a id="writes"></a>
## Review a management write

This example creates a department: groups.save with department set to true. Run it only when you mean to. Reading departments back needs units.list, and the receipt needs commands.get.

Save the command before sending so retries reuse its ID, request key and input.

### How management writes work

- Writes return {receipt, result}.
- An identical retry returns the same receipt with result: null and doesn’t repeat the action.
- Different input under the same request key is a conflict.
- Sensitive actions require confirm set to the operation name.
- If unsure whether a call succeeded, read the current resources first.

### Programs and runs

- Programs start paused. Activating one turns on its schedule.
- Expected revisions stop you overwriting changes you haven’t seen.
- Provider handoffs and delivery results need their own readback.

```ts
const command = {
  kind: 'list' as const,
  name: 'Customer Success',
  department: true,
  confirm: 'groups.save' as const,
  requestKey: 'create-customer-success-2026-10',
};
// Persist command before sending, so retries reuse the exact input.
const saved = await shuffl.groups.save(command);
const receipt = await shuffl.commands.get({ id: saved.receipt.id });
const departments = await shuffl.units.list({});
```

- [groups.save permission](https://shuffl.ai/docs/api-permissions#permission-groups-save)

- [Check the operation schema](https://shuffl.ai/api/v1/openapi.json)

<a id="company"></a>
## Manage company settings with a company key

A company owner or administrator creates a company key (shuffl_company_) in Settings → API keys and webhooks → Company keys.

The same Shuffl client takes it in place of a workspace key and calls the operations under /api/v1/company through shuffl.company, with the same types, errors and HTTPS rules.

company.current() returns the key’s company and grants with any company key. Every other method needs its own grant, such as company.members.list, which paginates like users.list.

Writes such as company.members.remove run once and take no requestKey.

A company key can’t call the workspace methods, and a workspace key can’t call shuffl.company. The SDK refuses the wrong kind with UNAUTHORIZED before sending anything.

```ts
import { Shuffl } from '@shuffl/sdk';

const shuffl = new Shuffl({ apiKey: process.env.SHUFFL_COMPANY_KEY! });
const { permissions } = await shuffl.company.current();
const page = await shuffl.company.members.list({ limit: 100 });
const next = page.nextCursor
  ? await shuffl.company.members.list({ cursor: page.nextCursor, limit: 100 })
  : null;
```

- [Company settings API reference](https://shuffl.ai/docs/api-reference/company)

- [Company keys and their grants](https://shuffl.ai/docs/api-permissions#company-keys)

<a id="errors"></a>
## Handle errors and revoke access

SDK errors carry an oRPC code and HTTP status. The error codes list says what each one means and what to check.

### Retrying

- The SDK never retries writes. Retry with the same command input, then check the receipt or resource after an interrupted call.
- Don’t retry automatically with broader permissions.
- To cancel, pass an abort signal in the call options.

### Revoking

- Revoking a key in Workspace settings → API keys disables it and any keys issued from it. Deleting the local secret doesn’t.
- To change permissions, create a replacement and revoke the old one.
- Recreating a membership doesn’t revive an old key.

- [Error codes](https://shuffl.ai/docs/api-reference#errors)

- [Open API keys](https://shuffl.ai/app/settings/api-keys)

- [Endpoint permission reference](https://shuffl.ai/docs/api-permissions)

## Related guides

- [Use Shuffl with your agent](https://shuffl.ai/docs/agent-quick-start)

- [REST API reference](https://shuffl.ai/docs/api-reference)

- [SDK and CLI recipes](https://shuffl.ai/docs/sdk-cookbook)

- [API endpoint permissions](https://shuffl.ai/docs/api-permissions)

- [Use the Shuffl CLI](https://shuffl.ai/docs/cli)

- [Connect an MCP client](https://shuffl.ai/docs/mcp)

Agent documentation index: https://shuffl.ai/llms.txt

