Basic usage
This guide covers the main things you are likely to do first with the SDK.
Setup
import { createRadiosoClient, RadiosoError } from '@radioso/typescript-sdk'
const client = createRadiosoClient({
apiToken: process.env.RADIOSO_API_TOKEN!,
})Documents
List documents:
const documents = await client.documents.list({ limit: 20 })Create a document:
const queued = await client.documents.create({
title: 'FAQ',
content: 'Radioso can answer questions about uploaded content.',
source: {
kind: 'website',
url: 'https://example.com/docs',
},
metadata: {
category: 'support',
published: true,
},
})Import a file:
import { readFile } from 'node:fs/promises'
const file = await readFile('./handbook.pdf')
const imported = await client.documents.importFile({
file,
filename: 'handbook.pdf',
title: 'Support handbook',
mimeType: 'application/pdf',
})Fetch a document:
const document = await client.documents.get('document-id')Update a document:
await client.documents.update('document-id', {
title: 'FAQ v2',
content: 'Updated content',
metadata: {
category: 'support',
version: 2,
},
})Delete a document:
await client.documents.delete('document-id')Search documents:
const search = await client.documents.search({
query: 'answers about uploaded content',
})List document search history:
const history = await client.documents.listHistory({ limit: 10 })Replay one historical search:
const replay = await client.documents.getHistory('search-id')Reprocess a document:
await client.documents.reprocess('document-id')Force enrichment for a single reprocess run:
await client.documents.reprocess('document-id', {
documentEnrichmentOverride: 'on',
})Reprocess all eligible documents for one source:
await client.documents.reprocessSource('source-id', {
documentEnrichmentOverride: 'on',
})Settings
Read ingestion settings:
const ingestion = await client.settings.getIngestion()Update ingestion settings:
Supported chunking strategies are 'fixed_window', 'structured_semantic', and 'recursive_text'.
await client.settings.updateIngestion({
chunkingStrategy: 'fixed_window',
fixedWindowChunkSize: 800,
fixedWindowChunkOverlap: 120,
structuredMinChunkSize: 400,
structuredMaxChunkSize: 1200,
documentEnrichmentEnabled: true,
})Queue workspace-wide reprocessing after an ingestion change:
await client.settings.reprocessIngestion()Workspace reprocessing also accepts a one-run enrichment override:
await client.settings.reprocessIngestion({
documentEnrichmentOverride: 'off',
})Read general settings:
const general = await client.settings.getGeneral()Update general settings:
await client.settings.updateGeneral({
anonymousChatEnabled: true,
})Skills
The SDK exposes the read-only product skills catalog. Agent-authored skills are configured through the agent skills REST endpoints directly — the SDK doesn’t wrap them with typed methods, so call them with fetch or another HTTP client using the same bearer token, as shown below. Those agent skills are named capability instances such as retrieve, email, slack_post, webhook_call, mcp_tool, and notify.
List skills:
const catalog = await client.skills.list()Read one catalog skill:
const retrievalAnswer = await client.skills.get('retrieval.answer')
console.log(retrievalAnswer.contractReferences)To author an agent skill over REST:
const capabilities = await fetch(`/api/v1/agents/${agentId}/skill-capabilities`)
const created = await fetch(`/api/v1/agents/${agentId}/skills`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: 'send_followup',
capability: 'email',
target: { kind: 'customer_email_connection', id: connectionId },
config: {
mode: 'draft',
exposedInputs: { to: { slotBinding: 'email' }, bodyText: { slotBinding: 'message' } },
boundInputs: { subject: 'Follow-up' },
},
invocationMode: 'routine_named',
enabled: true,
}),
})Retrieval answer settings live on the default-answer retrieve skill. Suggested questions are part of that skill’s config. Contact escalation is a notify skill, and routine completion export is a webhook_call skill.
Temporal retrieval fields such as temporalStructuredLookupEnabled, temporalBoostUpcomingEnabled, and temporalDeterministicSortEnabled are per-agent retrieval.answer skill settings. They default to on and control date-aware event lookup, boosting, and ordering when enriched chunks contain dateFrom and dateTo metadata.
When you call retrieval answer through the REST contract, the response activityTrace includes shape diagnostics. The trace graph is the debug surface. Check activitySummary.shapeName, activitySummary.queryShape, activitySummary.resolvedSteps, and the shape_selection stage to see how the answer was retrieved.
Agents
Each workspace has a default agent. Chat calls use that agent when agentId is omitted.
List agents:
const agents = await client.agents.list()
const defaultAgent = agents.agents.find((agent) => agent.isDefault)Create a direct-only agent:
const direct = await client.agents.create({
name: 'Direct support',
customInstruction: 'Answer from the configured instructions. Do not cite documents.',
retrievalEnabled: false,
})Use a specific agent in chat:
const response = await client.chat.create({
agentId: direct.id,
message: 'How should I answer a general support question?',
stream: false,
})Agents use the retrieval pipeline through their default-answer retrieve skill. Edit that skill to configure retrieval behavior for one agent. Omitted fields inherit system/model defaults, and the dashboard shows those inherited values inline before saving only explicit overrides. Direct-only agents answer from their own instructions and return retrieval diagnostics with retrievalInvoked: false.
Agent authoring
Authoring surfaces are available with a workspace API token. A script can build and configure an agent the same way the dashboard does, then chat with it, using one token. Authoring calls are namespaced under client.agents.* and take the agent id first.
Author a routine as portable markdown and publish it:
const draft = await client.agents.routines.createPortable(agentId, {
grammarVersion: 1,
content: '---\nname: Book a demo\ntrigger: the visitor asks for a demo\n---\nAsk for their @work_email.\nThen call #book_demo and confirm the time.',
})
await client.agents.routines.publish(agentId, draft.routineId)Create a directive that steers behavior by condition:
await client.agents.directives.create(agentId, {
name: 'Escalate refunds',
condition: { kind: 'contextual', description: 'the customer asks for a refund' },
action: 'Offer to connect them with a human agent.',
})Define a context variable, set a scoped value, and enable it on an agent:
const { contextVariable } = await client.contextVariables.create({
name: 'plan_tier',
description: "The visitor's current plan",
valueType: 'string',
trustTier: 'unverified',
sensitivity: 'normal',
defaultSurfacing: 'always',
})
await client.contextVariables.upsertValue(contextVariable.id, { scope: { type: 'customer', id: customerId }, data: 'pro' })
await client.agents.contextVariables.upsert(agentId, contextVariable.id, { source: 'pushed', surfacing: 'always', enabled: true })Bind a skill so a routine can act. Capability-specific bindings have their own namespaces: client.agents.emailSkills, client.agents.externalSkills, client.agents.webhookSkills, client.agents.slackSkills, client.agents.mcpConnections, and client.agents.mcpConverseGrants.
const skill = await client.agents.skills.create(agentId, {
name: 'send_followup',
capability: 'email',
target: { kind: 'customer_email_connection', id: connectionId },
config: { mode: 'draft', exposedInputs: { to: { slotBinding: 'email' } }, boundInputs: { subject: 'Follow-up' } },
invocationMode: 'routine_named',
enabled: true,
})Non-streaming chat
SDK chat methods target the assistant chat surface. Use them for human-facing agent conversations that should keep history and may answer directly or with retrieval-backed evidence.
const response = await client.chat.create({
message: 'What does the FAQ say about uploaded content?',
stream: false,
})
console.log(response.answer)Streaming chat
client.chat.stream() returns events one at a time. Check event.type and handle each case.
for await (const event of client.chat.stream({
message: 'Summarize the FAQ',
})) {
if (event.type === 'conversation') {
continue
}
if (event.type === 'chunk') {
continue
}
if (event.type === 'done') {
continue
}
if (event.type === 'error') {
throw event.error
}
}List chat history:
const conversations = await client.chat.listHistory({ limit: 20 })Fetch one historical conversation:
const conversation = await client.chat.getHistoryConversation('conversation-id', { limit: 50 })Read the latest conversation after listing history:
const recent = await client.chat.listHistory({ limit: 10 })
const latest = recent.conversations[0]
if (latest) {
const detail = await client.chat.getHistoryConversation(latest.id)
console.log(detail.messages)
}Error handling
The SDK turns request failures into RadiosoError, so you can handle them in one place.
try {
await client.documents.list()
} catch (error) {
if (error instanceof RadiosoError) {
if (error.status === 401) {
// refresh or replace the API token
}
} else {
throw error
}
}Notes
baseUrldefaults tohttps://api.radioso.ai, exported asDEFAULT_BASE_URL. Set it tohttps://api-us.radioso.aior your own origin for a self-hosted deployment — a workspace API token only works against the instance that issued it.- The SDK sends the API token as
Authorization: Bearer <token>. - Streaming chat is layered on top of
POST /api/v1/assistant/chat, withstream: true. - Skill discovery is exposed through
client.skills.list()andclient.skills.get(name). The catalog describes current assistant, retrieval, document, and MCP contracts; it does not execute skills directly. - Retrieval-only clients should use the REST retrieval surfaces,
POST /api/v1/retrieval/searchandPOST /api/v1/retrieval/answer, when they do not want assistant persona or assistant-owned chat history. These calls run on system defaults and keep supporting per-callmetadataFilter.retrieval.answerresponses expose shape and resolved-step diagnostics throughactivityTrace; callers do not select shapes directly. - Shared workspace settings are exposed by the REST platform settings resource,
GET /api/v1/settingsandPUT /api/v1/settings, with assistant and channel settings. Ingestion settings are exposed separately through the settings API. - Workspace creation, rename, and deletion are not exposed because those routes are session-authenticated rather than token-authenticated.
- Run
pnpm run syncintypescript-sdk/after backend API changes so the generated types stay up to date.