TypeScript SDK
@naturali/sdk is a typed client for the whole API, generated from the
OpenAPI specs. Every operation, every schema — nothing is
hand-written per endpoint, so the SDK cannot lag the contract.
pnpm add @naturali/sdk
Quick start
import { NaturaliClient } from '@naturali/sdk';
const naturali = new NaturaliClient({
token: process.env.NATURALI_TOKEN,
});
const { data, error } = await naturali.agents.listAgents({
path: { project_id: 'proj_V1StGXR8Z5jdHi6B' },
query: { limit: 10 },
});
if (error) {
console.error(error.error.code); // 'access_denied', 'budget_exceeded', …
} else {
console.log(data.data);
}
A method never throws on a non-2xx response. It resolves to { data, error },
so the error envelope and its
machine-readable code arrive as typed data — the fail-closed codes are values
you branch on, not exceptions you catch.
Client options
The client always talks to https://api.naturali.ai — the only naturali.ai API
origin, and not configurable.
| Option | Purpose |
|---|---|
token | Sent as Authorization: Bearer …. A nat_sk_… API key or a session access JWT from Auth. |
headers | Merged last, so it overrides the header above or adds your own. |
There is no project option. A project-scoped operation names its project in
the path (project_id), and that path parameter is the only thing the API
authorizes against — there is no header notion of a "current project" to keep
in sync with it.
Create one client and reuse it. Two clients never share state, so a request-scoped client per user session is safe.
Call shape
Arguments follow the spec's structure — path, query, body — using the same
snake_case field names as the wire:
await naturali.sessions.addSessionMessage({
path: { project_id, agent_id, session_id },
body: { message: 'What is the capital of France?' },
});
await naturali.knowledge.queryKnowledgeCollection({
path: { project_id, collection_id },
body: { query: 'refund policy', limit: 5 },
});
Method names are the spec's operationIds, so the
API Reference page for an operation names the method that
calls it.
Resources
One property per resource, each mirroring a module. This table is generated
from clients/sdk/src/naturaliClient.ts — the same file
naturaliClient.test.ts pins against the SDK's own generated exports — by
pnpm generate-all (see scripts/generateSdkResourcesTable.ts), so it cannot
go stale the way a hand-written enumeration did (#343).
| Property | Module |
|---|---|
activity | Activity |
actors | Actors |
agents | Agents |
agentVersions | Agents → versioning and staged rollout |
aiProviders | AI Providers |
apiKeys | API Keys |
approvals | Approvals |
assistant | Assistant |
auditLog | Audit Log |
auth | Auth |
chains | Chains |
channels | Channels — and channel routes, addresses and channel kinds, which share its tag |
conversations | Conversations |
deciders | Deciders |
documents | Documents |
embeddings | Embeddings |
evaluations | Evaluations |
exceptions | Exceptions |
files | Files |
formations | Formations |
generations | Generations |
guardrails | Guardrails |
ingestionRules | Ingestion Rules |
installs | Marketplace |
knowledge | Knowledge |
listings | Marketplace |
memories | Memories |
memoryRules | Memory Rules |
memoryStores | Memory Stores |
metadataSchemas | Metadata Schemas |
modelRoutes | Model Routes |
models | Models |
orchestrations | Orchestrations |
projects | Projects |
quotas | Quotas |
secrets | Secrets |
sessions | Sessions |
tasks | Tasks |
tools | Tools |
traces | Traces |
triggers | Triggers |
users | Users |
webhooks | Webhooks |
workflows | Workflows |
naturali.http exposes the underlying HTTP client for interceptors or a one-off
request such as naturali.http.get({ url: '/health' }).
Types
Every schema in the specs is exported:
import type { Agent, AgentCreate, ErrorResponse } from '@naturali/sdk';
Every date-time field — created_at, updated_at, completed_at, and any
other field typed date-time in the spec — is handed over as a Date object;
every other field matches the wire shape exactly.
Pagination
List operations are cursor-based. Follow next_cursor until it comes back
null:
let cursor: string | undefined;
do {
const { data } = await naturali.agents.listAgents({
path: { project_id },
query: { limit: 100, cursor },
});
for (const agent of data?.data ?? []) {
console.log(agent.id);
}
cursor = data?.next_cursor ?? undefined;
} while (cursor);
Browser use
Pass a session JWT, never an API key — a nat_sk_… secret does not belong
in a browser (see Auth for the session flow).
Versioning
@naturali/sdk shares its version with the API and with
@naturali/cli — one number, released together. The SDK is generated
from the API's OpenAPI specs, so @naturali/sdk@0.28.0 is the client for API
0.28.0, and installing it never leaves you guessing which contract you are
holding.
A consequence worth knowing: a bump can mean the API gained a resource, not that anything in the SDK's own surface changed. Releases are automatic on merge, so there is no release cadence to follow — pin what you need, or track the latest and read the changelog.