JavaScript & TypeScript SDK
The official JavaScript and TypeScript library for the RSFlowHub AI API. Zero dependencies, full types, and it runs in Node.js, Next.js, Express, Bun, Deno, Cloudflare Workers and Vercel Edge.
Direct System Definition: RSFlowHub JavaScript SDK
rsflowhub is the official npm package for RSFlowHub. Install it with npm install rsflowhub, set RSFLOWHUB_API_KEY and call any API as client.ai.slugInCamelCase({...}). It uses the built-in fetch (Node.js 18+), ships ESM, CommonJS and TypeScript types, throws typed errors by error code, retries 429 and 5xx answers, supports AbortSignal, and refuses to run in a browser so your key stays secret. New APIs work without updating the package.
| Package | rsflowhub on npm |
|---|---|
| Install | npm install rsflowhub |
| Requires | Node.js 18 or newer, Bun, Deno, Cloudflare Workers, Vercel Edge (no dependencies) |
| Works with | Next.js, React (server side), Express, NestJS, Nuxt, SvelteKit |
| Source | github.com/RSFlowHub/rsflowhub-node (MIT license) |
Install
npm install rsflowhub
# yarn add rsflowhub · pnpm add rsflowhub · bun add rsflowhub
# Deno: import { RSFlowHub } from 'npm:rsflowhub'
Create an API key in your dashboard and put it in your server environment (.env.local for Next.js). The SDK reads RSFLOWHUB_API_KEY automatically.
RSFLOWHUB_API_KEY=rfh_your_api_key
Your API key spends your credits and anything shipped to the browser is public. The SDK throws if it starts in a browser, unless you pass
dangerouslyAllowBrowser: true (for internal tools only). React, Vue and Svelte components should call your own server route, as in the Next.js and Express examples below.
Make your first call
import { RSFlowHub } from 'rsflowhub';
// CommonJS: const { RSFlowHub } = require('rsflowhub');
const client = new RSFlowHub(); // reads process.env.RSFLOWHUB_API_KEY
const res = await client.ai.sentimentAnalysis({
text: 'Setup took two minutes and support answered right away.',
});
console.log(res.result.sentiment); // "positive"
console.log(res.result); // the whole AI output
console.log(res.requestId); // "req_..." quote it when you contact support
The response object
Every successful call returns an RSFlowHubResponse built from the API envelope {success, request_id, data, meta}. AI endpoints put their output in data.result.
| Use | What you get |
|---|---|
res.result | The AI output (data.result), or data when there is no result (knowledge bases, chatbots) |
res.data, res.get('result.aspects.0.name', fallback) | Anything under data, with optional dot-path reads |
res.requestId, res.meta | Server request id and { version, timestamp } |
res.status, res.headers | HTTP status and the Headers object |
res.rateLimit | { limit, remaining, reset } from the X-RateLimit-* headers |
Calling any API
Methods are not hard-coded. The method is the endpoint's slug in camelCase, grouped by resource: client.ai, client.knowledgeBases, client.chatbots, client.faqCollections and client.account. See every endpoint in the API reference.
// The method name is the endpoint slug in camelCase
await client.ai.translation({ text: 'Hello', target_language: 'French' });
await client.ai.piiRedaction({ text: 'Call me on 555-0100' });
await client.ai.ticketAnalyze({ ticket: 'My order never arrived' });
// An AI API released after you installed the SDK works the same way
await client.ai.brandNewEndpoint({ text: '...' });
// ...and always type-checks this way (also under noUncheckedIndexedAccess)
await client.call('ai', 'brandNewEndpoint', { text: '...' });
// A resource the SDK does not know yet
await client.resource('webhooks').list!();
// Any route: method, path, body, query, options
const usage = await client.request('GET', '/account/usage', undefined, { page: 2 });
Path ids and file uploads
import { openAsBlob } from 'node:fs';
// Ids in the URL come first, then the body, then per-call options
await client.knowledgeBases.ask(12, { question: 'Refund policy?' });
await client.chatbots.getConversation(3, 'conv_abc123');
// A Blob, File, Uint8Array / Buffer or ArrayBuffer makes it a file upload
await client.knowledgeBases.createDocument(12, {
file: await openAsBlob('refund-policy.pdf'), // Node 20+; or a File from an <input> upload
metadata: { source: 'help-center' }, // sent as metadata[source]
});
Knowledge bases (RAG) and chatbots
Build a searchable help center from your documents and FAQs, then answer questions from it or run a chatbot on top of it.
// 1. A knowledge base for your help center
const kb = await client.knowledgeBases.create({ name: 'Help center' });
const kbId = kb.get<number>('knowledge_base.id');
// 2. Add content: a document, or a single FAQ item
await client.knowledgeBases.createDocument(kbId, { file: await openAsBlob('refund-policy.pdf') });
await client.knowledgeBases.createItem(kbId, {
content: 'How long do refunds take?',
answer: 'Refunds reach your card in 5 to 7 business days.',
});
// 3. Ask questions grounded in that content
const answer = await client.knowledgeBases.ask(kbId, { question: 'When will I get my money back?' });
console.log(answer.result);
// A chatbot (created in the dashboard or with client.chatbots.create({...}))
const reply = await client.chatbots.sendMessage(chatbotId, { message: 'Where is my order?' });
const conversationId = reply.data.conversation_id;
Errors
Every API error is an APIError subclass chosen by the error code in the error envelope, with status, code, message, details and requestId. Everything extends RSFlowHubError.
import {
APIError, ConnectionError, InsufficientCreditsError, RateLimitError, ValidationError,
} from 'rsflowhub';
try {
await client.ai.textSummarize({ text: article });
} catch (error) {
if (error instanceof ValidationError) {
console.log(error.errors); // { text: ['The text field is required.'] }
} else if (error instanceof InsufficientCreditsError) {
console.log(error.requiredCredits, error.availableCredits);
} else if (error instanceof RateLimitError) {
console.log(error.retryAfter); // already retried maxRetries times
} else if (error instanceof APIError) {
console.log(error.status, error.code, error.message, error.requestId);
} else if (error instanceof ConnectionError) {
console.log('network problem or timeout');
} else {
throw error;
}
}
| Error class | Status | Error codes | Extra |
|---|---|---|---|
AuthenticationError | 401 | API_KEY_MISSING, INVALID_API_KEY | |
InsufficientCreditsError | 402 | INSUFFICIENT_CREDITS | requiredCredits, availableCredits |
PermissionDeniedError | 403 | SPENDING_LIMIT_REACHED, USER_INACTIVE | |
NotFoundError | 404 | ENDPOINT_NOT_FOUND | |
ValidationError | 422 | VALIDATION_ERROR | errors |
RateLimitError | 429 | RATE_LIMIT_EXCEEDED | retryAfter |
InternalServerError | 500 | INTERNAL_SERVER_ERROR | |
ServiceUnavailableError | 502 / 503 | AI_PROVIDER_ERROR, PROVIDER_UNAVAILABLE | |
ConnectionError | none | Network failure or timeout | |
AbortError | none | Your AbortSignal fired |
Options, retries and timeouts
429, 502, 503, 504 and network failures are retried twice with backoff, respecting Retry-After; those answers are never charged. Other errors are thrown at once. Limits and the credit formula are on Rate limits & credits.
const client = new RSFlowHub({
apiKey: process.env.RSFLOWHUB_API_KEY, // default
baseURL: 'https://rsflowhub.com/api/v1', // or RSFLOWHUB_BASE_URL
timeout: 60_000, // ms per attempt
maxRetries: 2, // for 429, 502, 503, 504 and network errors
headers: {}, // extra headers on every request
fetch: customFetch, // proxies, logging, tests
liveSpec: true, // look up unknown non-AI methods in the OpenAPI document
});
// Per call: the last argument
const controller = new AbortController();
await client.ai.chat({ message: 'Hi' }, { signal: controller.signal, timeout: 10_000, maxRetries: 0 });
await client.knowledgeBases.get(12, { signal: controller.signal });
res.rateLimit; // { limit: 60, remaining: 59, reset: null }
Next.js and React
Call RSFlowHub from a route handler or a server action. Both run on the server, so the key never reaches the browser.
// app/api/moderate/route.ts (Next.js App Router, runs on the server)
import { RSFlowHub, APIError } from 'rsflowhub';
const client = new RSFlowHub();
export async function POST(request: Request) {
const { comment } = await request.json();
try {
const res = await client.ai.commentModerate({ comment });
return Response.json(res.result);
} catch (error) {
if (error instanceof APIError) {
return Response.json({ error: error.message }, { status: error.status });
}
throw error;
}
}
// app/actions.ts (server action)
'use server';
import { RSFlowHub } from 'rsflowhub';
const client = new RSFlowHub();
export async function summarize(text: string) {
const res = await client.ai.textSummarize({ text, format: 'bullet_points' });
return res.result;
}
// In a client component: const summary = await summarize(article);
'use client';
// React component: calls YOUR route, never RSFlowHub directly
import { useState } from 'react';
export function ModerationCheck() {
const [verdict, setVerdict] = useState<string | null>(null);
async function check(comment: string) {
const res = await fetch('/api/moderate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ comment }),
});
const data = await res.json();
setVerdict(data.error ?? JSON.stringify(data));
}
return (
<form onSubmit={(e) => { e.preventDefault(); check(new FormData(e.currentTarget).get('c') as string); }}>
<textarea name="c" />
<button>Check</button>
{verdict && <p>{verdict}</p>}
</form>
);
}
The same pattern works in Remix loaders and actions, Nuxt server routes, SvelteKit +server.ts files and Astro endpoints.
Express, NestJS and other Node.js servers
Create one client when the server starts and reuse it in every request:
import express from 'express';
import { RSFlowHub, APIError } from 'rsflowhub';
const app = express();
const client = new RSFlowHub();
app.use(express.json());
app.post('/api/ask', async (req, res) => {
try {
const answer = await client.knowledgeBases.ask(process.env.KB_ID!, { question: req.body.question });
res.json(answer.data);
} catch (error) {
if (error instanceof APIError) return res.status(error.status).json({ error: error.message });
res.status(500).json({ error: 'Something went wrong' });
}
});
app.listen(3001);
Edge runtimes, Bun and Deno
The SDK has no dependencies and only needs fetch, so it runs on Cloudflare Workers, Vercel Edge Functions, Bun and Deno unchanged:
// Cloudflare Workers / Vercel Edge: the SDK only needs fetch
import { RSFlowHub } from 'rsflowhub';
export default {
async fetch(request: Request, env: { RSFLOWHUB_API_KEY: string }) {
const client = new RSFlowHub({ apiKey: env.RSFLOWHUB_API_KEY });
const { text } = (await request.json()) as { text: string };
const res = await client.ai.spamDetection({ text });
return Response.json(res.result);
},
};
TypeScript types and staying up to date
- New AI APIs work at once: they are called by name, no upgrade needed.
- Other new routes are looked up in the public OpenAPI document the first time you call them.
- Types: every method has typed parameters (for example
AiSentimentAnalysisParams) with limits in the JSDoc, regenerated weekly from the OpenAPI document.npm update rsflowhubpicks them up. - The full method list is in docs/ENDPOINTS.md.
Related
- All official SDKs and how they stay up to date
- Error codes and rate limits & credits
- Authentication & API keys
- OpenAPI document (every request and response)
- Examples repository
- RSFlowHub PHP SDK (
composer require rsflowhub/rsflowhub-php) - RSFlowHub Python SDK (
pip install rsflowhub)