Skip to content
RSFlowHub
Developer docs API v1 Stable OpenAPI SDKs Playground
JavaScript & TypeScript SDK for the RSFlowHub AI API (Node.js, Next.js)

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.

Packagersflowhub on npm
Installnpm install rsflowhub
RequiresNode.js 18 or newer, Bun, Deno, Cloudflare Workers, Vercel Edge (no dependencies)
Works withNext.js, React (server side), Express, NestJS, Nuxt, SvelteKit
Sourcegithub.com/RSFlowHub/rsflowhub-node (MIT license)

Install

terminal Bash
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.

.env.local .env
RSFLOWHUB_API_KEY=rfh_your_api_key
Never in browser code
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

sentiment.ts TypeScript
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.

UseWhat you get
res.resultThe 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.metaServer request id and { version, timestamp }
res.status, res.headersHTTP 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.

calling.ts TypeScript
// 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

ids-and-files.ts TypeScript
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.

help-center.ts TypeScript
// 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.

errors.ts TypeScript
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 classStatusError codesExtra
AuthenticationError401API_KEY_MISSING, INVALID_API_KEY
InsufficientCreditsError402INSUFFICIENT_CREDITSrequiredCredits, availableCredits
PermissionDeniedError403SPENDING_LIMIT_REACHED, USER_INACTIVE
NotFoundError404ENDPOINT_NOT_FOUND
ValidationError422VALIDATION_ERRORerrors
RateLimitError429RATE_LIMIT_EXCEEDEDretryAfter
InternalServerError500INTERNAL_SERVER_ERROR
ServiceUnavailableError502 / 503AI_PROVIDER_ERROR, PROVIDER_UNAVAILABLE
ConnectionErrornoneNetwork failure or timeout
AbortErrornoneYour 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.

options.ts TypeScript
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
// 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:

server.ts TypeScript
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:

worker.ts TypeScript
// 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 rsflowhub picks them up.
  • The full method list is in docs/ENDPOINTS.md.

JavaScript / TypeScript SDK Questions

Run npm install rsflowhub (or yarn, pnpm or bun add rsflowhub), set RSFLOWHUB_API_KEY and create a client with new RSFlowHub(). It works in Node.js 18+, Bun, Deno and edge runtimes, and ships ESM, CommonJS and TypeScript types.

Use it on the server: a Next.js route handler or server action, a Remix loader, an Express route. Your API key spends your credits and browser code is public, so the SDK refuses to start in a browser unless you pass dangerouslyAllowBrowser. Your React components call your own route.

No. AI endpoints are called by name, for example client.ai.newEndpointName({...}), so new APIs work immediately. Other new routes are looked up in the public OpenAPI document the first time you call them. Updating only adds TypeScript types for the new methods.

Yes. Every method has typed parameters generated from the OpenAPI document, with limits in the JSDoc, and every error is a typed class such as ValidationError or InsufficientCreditsError. Under noUncheckedIndexedAccess, call brand-new methods with client.call('ai', 'newEndpoint', {...}).

Pass per-call options as the last argument: client.ai.chat({ message }, { signal: controller.signal, timeout: 10_000, maxRetries: 0 }). An aborted signal throws AbortError, a timeout throws ConnectionError.

Ready to build?

Create your free account and make your first API call in minutes.

RSFlowHub

Sign in to continue

You'll come straight back to this page.

Forgot password?

New to RSFlowHub? Create a free account