Skip to navigation

TypeScript SDK

Call the Rydhm API from Node.js and TypeScript with typed requests, responses and errors.

@rydhm/sdk is the TypeScript client for the Rydhm API. It is generated from the API’s OpenAPI document, so its types match what the API accepts and returns.

The SDK isn’t published to npm yet. Until it is, call the API over HTTP: every example in these docs has a curl version, and the API reference shows each request in TypeScript.

Install

npm install @rydhm/sdk

The SDK uses the platform’s fetch. Run it on your server, never in a browser: it needs your API key.

Initialize

import { RydhmClient } from "@rydhm/sdk";
const client = new RydhmClient({ apiKey: process.env.RYDHM_API_KEY });

If you pass no apiKey, the client reads RYDHM_API_KEY from the environment.

OptionDefaultWhat it does
apiKeyRYDHM_API_KEYYour API key, rydhm_live_….
environmentRydhmEnvironment.Productionhttps://api.rydhm.ai.
baseUrlAnother address for the API, such as your own proxy. Give the host only, without /v1: the SDK adds the path. Overrides environment.
timeoutInSeconds60How long a request may take.
maxRetries2How many times a failed request is retried. See Retries.
fetchglobal fetchA fetch to use instead.

How methods are named

Methods are grouped by resource: client.experts, client.knowledge, client.conversations, client.chat, client.voice, client.meetings, client.tools, client.analytics. Path parameters come first, in order. Query parameters and the body go together in one request object after them:

await client.experts.get(expertId); // GET /v1/agents/{id}
await client.knowledge.search(expertId, { q: "refunds" }); // GET /v1/agents/{id}/kb/search?q=refunds
await client.chat.send(conversationId, { content: "Hi" }); // POST /v1/chat/conversations/{id}/messages

Every method needs a scope on your key. See Scopes.

Common tasks

List experts

const experts = await client.experts.list();
for (const expert of experts) console.log(expert.id, expert.name, expert.visibility);
const expert = await client.experts.get(experts[0].id);

Scope: experts:read.

Start a chat and send a message

const conversation = await client.chat.start({ agentId: expertId });
const message = await client.chat.send(conversation.id, {
content: "What can you help with?",
});

chat.send stores your message. Read the expert’s reply from the stream. Scope: chat:write.

Stream the reply

const stream = await client.chat.stream(conversation.id);
let reply = "";
for await (const frame of stream) {
switch (frame.type) {
case "sources":
console.log(`Grounded in ${frame.count} passages`);
break;
case "delta":
reply += frame.content;
process.stdout.write(frame.content);
break;
case "error":
throw new Error(frame.message);
case "done":
break;
}
}

Each frame is typed by its type: sources, delta, tool_call, tool_result, message, done or error. See Stream the reply.

To continue the conversation, call chat.send and chat.stream again with the same id.

Read a conversation

const messages = await client.conversations.get(conversation.id);
for (const m of messages) console.log(`${m.role}: ${m.content}`);
const recent = await client.conversations.list({ agentId: expertId }); // the 50 most recent

Scope: conversations:read.

Search knowledge

const hits = await client.knowledge.search(expertId, { q: "refund policy" });
for (const hit of hits) console.log(hit.score.toFixed(2), hit.text.slice(0, 80));

Scope: knowledge:read. mode (hybrid or vector) is optional.

Add a document

const doc = await client.knowledge.add(expertId, {
filename: "returns.md",
mimeType: "text/markdown",
content: "# Returns\n\nItems can be returned within 30 days.",
});
console.log(doc.status); // "ready": indexed before the call returns

Scope: knowledge:write. For a PDF, Word, Markdown, text or CSV file, use client.knowledge.addFile(expertId, { filename, data }) with the file as base64.

Send an expert to a meeting

const meeting = await client.meetings.join({
"Idempotency-Key": "crm-event-4821",
agentId: expertId,
joinUrl: "https://meet.google.com/abc-defg-hij",
mode: "speaker",
});

Scope: meetings:join. Always pass an idempotency key: a retry with the same key returns the first result instead of sending a second bot.

Errors

A non-2xx response throws. Each common status has its own class, and all of them extend RydhmError:

import { Rydhm, RydhmClient, RydhmError } from "@rydhm/sdk";
try {
await client.experts.create({ name: "Support" });
} catch (err) {
if (err instanceof Rydhm.ForbiddenError) {
console.log(err.body.error); // "insufficient_scope"
console.log(err.body.missingScopes); // ["experts:write"]
} else if (err instanceof RydhmError) {
console.log(err.statusCode, err.body);
} else {
throw err;
}
}
ClassStatus
Rydhm.UnauthorizedError401
Rydhm.PaymentRequiredError402
Rydhm.ForbiddenError403
Rydhm.ConflictError409, such as code: "stale_edit" when someone saved an expert after you loaded it
Rydhm.TooManyRequestsError429
RydhmErrorAny other non-2xx status
RydhmTimeoutErrorThe request took longer than the timeout

err.body is the API’s error body, with error (or code), message and requestId.

Retries

By default the SDK retries a request up to 2 times, with exponential backoff, when the response is 408, 429 or any 5xx. Change it for the whole client with maxRetries, or for one call:

await client.chat.start({ agentId: expertId }, { maxRetries: 0 });

Retries also apply to calls that start paid work. Always pass an Idempotency-Key to meetings.join, so a retried request can’t send a second bot.

Headers and request ids

.withRawResponse() gives you the response headers too, such as the request id and rate-limit state:

const { data, rawResponse } = await client.chat
.start({ agentId: expertId })
.withRawResponse();
console.log(rawResponse.headers.get("x-request-id"));
console.log(rawResponse.headers.get("x-ratelimit-remaining"));

Every method also takes request options as its last argument: timeoutInSeconds, maxRetries, abortSignal and extra headers.