Skip to content

Basic usage

This guide covers the main things you are likely to do first with the SDK.

Setup

typescript
import { createRadiosoClient, RadiosoError } from '@radioso/typescript-sdk'
 
const client = createRadiosoClient({
  apiToken: process.env.RADIOSO_API_TOKEN!,
})

Documents

List documents:

typescript
const documents = await client.documents.list({ limit: 20 })

Create a document:

typescript
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:

typescript
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:

typescript
const document = await client.documents.get('document-id')

Update a document:

typescript
await client.documents.update('document-id', {
  title: 'FAQ v2',
  content: 'Updated content',
  metadata: {
    category: 'support',
    version: 2,
  },
})

Delete a document:

typescript
await client.documents.delete('document-id')

Search documents:

typescript
const search = await client.documents.search({
  query: 'answers about uploaded content',
})

List document search history:

typescript
const history = await client.documents.listHistory({ limit: 10 })

Replay one historical search:

typescript
const replay = await client.documents.getHistory('search-id')

Reprocess a document:

typescript
await client.documents.reprocess('document-id')

Force enrichment for a single reprocess run:

typescript
await client.documents.reprocess('document-id', {
  documentEnrichmentOverride: 'on',
})

Reprocess all eligible documents for one source:

typescript
await client.documents.reprocessSource('source-id', {
  documentEnrichmentOverride: 'on',
})

Settings

Read ingestion settings:

typescript
const ingestion = await client.settings.getIngestion()

Update ingestion settings:

Supported chunking strategies are 'fixed_window', 'structured_semantic', and 'recursive_text'.

typescript
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:

typescript
await client.settings.reprocessIngestion()

Workspace reprocessing also accepts a one-run enrichment override:

typescript
await client.settings.reprocessIngestion({
  documentEnrichmentOverride: 'off',
})

Read general settings:

typescript
const general = await client.settings.getGeneral()

Update general settings:

typescript
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:

typescript
const catalog = await client.skills.list()

Read one catalog skill:

typescript
const retrievalAnswer = await client.skills.get('retrieval.answer')
console.log(retrievalAnswer.contractReferences)

To author an agent skill over REST:

typescript
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:

typescript
const agents = await client.agents.list()
const defaultAgent = agents.agents.find((agent) => agent.isDefault)

Create a direct-only agent:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
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.

typescript
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.

typescript
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.

typescript
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:

typescript
const conversations = await client.chat.listHistory({ limit: 20 })

Fetch one historical conversation:

typescript
const conversation = await client.chat.getHistoryConversation('conversation-id', { limit: 50 })

Read the latest conversation after listing history:

typescript
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.

typescript
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

  • baseUrl defaults to https://api.radioso.ai, exported as DEFAULT_BASE_URL. Set it to https://api-us.radioso.ai or 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, with stream: true.
  • Skill discovery is exposed through client.skills.list() and client.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/search and POST /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-call metadataFilter. retrieval.answer responses expose shape and resolved-step diagnostics through activityTrace; callers do not select shapes directly.
  • Shared workspace settings are exposed by the REST platform settings resource, GET /api/v1/settings and PUT /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 sync in typescript-sdk/ after backend API changes so the generated types stay up to date.