Skip to main content

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.

OptionPurpose
tokenSent as Authorization: Bearer …. A nat_sk_… API key or a session access JWT from Auth.
headersMerged 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).

PropertyModule
activityActivity
actorsActors
agentsAgents
agentVersionsAgents → versioning and staged rollout
aiProvidersAI Providers
apiKeysAPI Keys
approvalsApprovals
assistantAssistant
auditLogAudit Log
authAuth
chainsChains
channelsChannels — and channel routes, addresses and channel kinds, which share its tag
conversationsConversations
decidersDeciders
documentsDocuments
embeddingsEmbeddings
evaluationsEvaluations
exceptionsExceptions
filesFiles
formationsFormations
generationsGenerations
guardrailsGuardrails
ingestionRulesIngestion Rules
installsMarketplace
knowledgeKnowledge
listingsMarketplace
memoriesMemories
memoryRulesMemory Rules
memoryStoresMemory Stores
metadataSchemasMetadata Schemas
modelRoutesModel Routes
modelsModels
orchestrationsOrchestrations
projectsProjects
quotasQuotas
secretsSecrets
sessionsSessions
tasksTasks
toolsTools
tracesTraces
triggersTriggers
usersUsers
webhooksWebhooks
workflowsWorkflows

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.