RootCX
Docs
Pricing
RootCX/RootCX
Introduction
What is RootCX?How it Works
Build
Getting StartedApplicationAI AgentIntegrationDeploying
Platform
CoreAuthenticationRBACData APISecret VaultJob QueueAudit LogReal-time Logs
Developers
QuickstartReact SDKBackend & RPCManifest ReferenceREST APISelf-Hosting
RootCXDevelopersSdk

React SDK

@rootcx/sdk

Official React hooks for interacting with the RootCX runtime from your frontend applications.

Installation

$ npm install @rootcx/sdk

$ pnpm add @rootcx/sdk

$ yarn add @rootcx/sdk

The SDK requires React 18+ and TypeScript 5+ (recommended). It is ESM-only and works with Next.js, Vite, and other modern bundlers.

[!INFO] Runtime URL Point the SDK at your project's API URL (e.g. https://<your-ref>.rootcx.com). If self-hosting, use http://localhost:9100.

// src/main.tsx
import { RuntimeProvider } from "@rootcx/sdk";
import App from "./App";

createRoot(document.getElementById("root")!).render(
  <RuntimeProvider baseUrl="https://<your-ref>.rootcx.com">
    <App />
  </RuntimeProvider>
);

useAuth

Manage authentication state -- login, registration, logout, and the current user.

import { useAuth } from "@rootcx/sdk";

const {
  user,            // AuthUser | null -- current user, null if not authenticated
  isAuthenticated, // boolean
  loading,         // boolean -- true while checking session or performing auth
  login,           // (username: string, password: string) => Promise<void>
  register,        // (data: RegisterInput) => Promise<void>
  logout,          // () => Promise<void>
} = useAuth();
// src/LoginPage.tsx
import { useAuth } from "@rootcx/sdk";
import { useState } from "react";

export function LoginPage() {
  const { login, loading } = useAuth();
  const [username, setUsername] = useState("");
  const [password, setPassword] = useState("");

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    await login(username, password);
    // On success, user state updates and isAuthenticated becomes true
  };

  return (
    <form onSubmit={handleSubmit}>
      <input value={username} onChange={e => setUsername(e.target.value)} />
      <input type="password" value={password} onChange={e => setPassword(e.target.value)} />
      <button type="submit" disabled={loading}>
        {loading ? "Signing in..." : "Sign in"}
      </button>
    </form>
  );
}

useAppCollection

Fetch and mutate a collection of records for a given entity. Data is loaded on mount and re-fetched after mutations.

import { useAppCollection } from "@rootcx/sdk";

const {
  data,       // T[] -- array of records
  total,      // number -- total record count
  loading,    // boolean
  error,      // string | null
  create,     // (data: Record<string, unknown>) => Promise<T>
  bulkCreate, // (data: Record<string, unknown>[]) => Promise<T[]>
  update,     // (id: string, data: Record<string, unknown>) => Promise<T>
  remove,     // (id: string) => Promise<void>
  refetch,    // () => void -- manually re-fetch
} = useAppCollection<T>("appId", "entityName", query?);

The optional query parameter accepts a QueryOptions object to filter and sort results:

useAppCollection<Contact>("crm", "contacts", {
  where: { stage: "lead" },
  orderBy: "created_at",
  order: "desc",
  limit: 50,
  offset: 0,
});
Option Type Description
where Record<string, unknown> Filter conditions.
orderBy string Column to sort by.
order "asc" | "desc" Sort direction.
limit number Max records to return.
offset number Number of records to skip.
// src/ContactsList.tsx
import { useAppCollection } from "@rootcx/sdk";

type Contact = {
  id: string;
  first_name: string;
  last_name: string;
  email: string;
  created_at: string;
};

export function ContactsList() {
  const { data, loading, error, create, remove } = useAppCollection<Contact>(
    "crm",
    "contacts"
  );

  const addContact = async () => {
    await create({
      first_name: "Bob",
      last_name: "Smith",
      email: "bob@example.com",
    });
    // data array updates automatically
  };

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error}</div>;

  return (
    <div>
      <button onClick={addContact}>Add Contact</button>
      <ul>
        {data.map(c => (
          <li key={c.id}>
            {c.first_name} {c.last_name} -- {c.email}
            <button onClick={() => remove(c.id)}>Delete</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

useAppRecord

Fetch and manage a single record by ID.

import { useAppRecord } from "@rootcx/sdk";

const {
  data,      // T | null
  loading,   // boolean
  error,     // string | null
  refetch,   // () => void -- manually re-fetch
  update,    // (data: Record<string, unknown>) => Promise<T>
  remove,    // () => Promise<void>
} = useAppRecord<T>("appId", "entityName", recordId); // recordId: string | null
// src/ContactDetail.tsx
import { useAppRecord } from "@rootcx/sdk";

export function ContactDetail({ id }: { id: string }) {
  const { data, loading, update } = useAppRecord<Contact>(
    "crm", "contacts", id
  );

  if (loading) return <Spinner />;
  if (!data) return <div>Not found</div>;

  return (
    <div>
      <h1>{data.first_name} {data.last_name}</h1>
      <button onClick={() => update({ email: "new@email.com" })}>
        Update email
      </button>
    </div>
  );
}

usePermissions

Check the current user's RBAC permissions for a given app. Use to conditionally show/hide UI elements.

import { usePermissions } from "@rootcx/sdk";

const {
  roles,       // string[] -- resolved roles for the current user
  permissions, // string[] -- flat list of permission strings
  can,         // (permission: string) => boolean
  loading,     // boolean
  error,       // string | null
  refetch,     // () => void
} = usePermissions("appId");
import { usePermissions } from "@rootcx/sdk";

export function ContactActions({ contactId }: { contactId: string }) {
  const { can } = usePermissions("crm");

  return (
    <div>
      {can("contacts.update") && (
        <button>Edit</button>
      )}
      {can("contacts.delete") && (
        <button className="text-red-500">Delete</button>
      )}
    </div>
  );
}

[!WARNING] Client-side only usePermissions is for UI convenience only. Never rely on client-side permission checks for security. The Core daemon enforces RBAC on every request server-side.

AuthGate

Pre-built authentication UI. Wrap your app to show a login/registration form when the user is not authenticated:

import { AuthGate } from "@rootcx/sdk";

function App() {
  return (
    <AuthGate>
      <MyProtectedApp />
    </AuthGate>
  );
}

Authorized

Conditionally render children based on RBAC permissions:

import { Authorized } from "@rootcx/sdk";

<Authorized appId="crm" permission="contacts.delete">
  <button>Delete Contact</button>
</Authorized>

useIntegration

Connect to and interact with a bound integration's actions:

import { useIntegration } from "@rootcx/sdk";

const {
  connected,         // boolean -- whether the integration is authenticated
  loading,           // boolean
  connect,           // () => Promise<{ type: string; schema?: Record<string, unknown> } | void>
  submitCredentials, // (credentials: Record<string, string>) => Promise<void>
  disconnect,        // () => Promise<void>
  call,              // (action: string, input?: Record<string, unknown>) => Promise<unknown>
} = useIntegration("myApp", "slack");

// Call an action once connected
await call("send_message", { channel: "#general", text: "Hello!" });

Direct HTTP client

For use cases not covered by the hooks, import the underlying HTTP client directly:

import { RuntimeClient } from "@rootcx/sdk";

const client = new RuntimeClient({ baseUrl: "https://<your-ref>.rootcx.com" });

// Authenticate
await client.login("alice", "password");

// CRUD
const contacts = await client.listRecords("crm", "contacts");
const created = await client.createRecord("crm", "contacts", {
  first_name: "Alice",
  email: "alice@example.com",
});

// RPC
const result = await client.rpc("crm", "sendWelcomeEmail", {
  contactId: created.id,
});

TypeScript types

Key types exported from @rootcx/sdk:

// Authenticated user
export interface AuthUser {
  id: string;
  username: string;
  email: string | null;
  displayName: string | null;
  createdAt: string;
}

// Login response
export interface LoginResponse {
  accessToken: string;
  refreshToken: string;
  expiresIn: number;
  user: AuthUser;
}

// Registration input
export interface RegisterInput {
  username: string;
  password: string;
  email?: string;
  displayName?: string;
}

// Effective permissions for a user in an app
export interface EffectivePermissions {
  roles: string[];
  permissions: string[];
}
PreviousQuickstartNextBackend & RPC

On this page

Installation
useAuth
useAppCollection
useAppRecord
usePermissions
AuthGate
Authorized
useIntegration
Direct HTTP client
TypeScript types