Skip to main content

Build Your First App

Learn to integrate the CyberPod SDK into your app with practical examples. This tutorial focuses on SDK usage patterns with minimal UI components.

What You'll Learn

  • Initialize and configure the SDK
  • Authenticate users and manage sessions
  • Fetch and display user data
  • Work with projects and files
  • Use AI chat completions with streaming
  • Handle notifications

Prerequisites

  • Node.js 18+
  • Basic React/TypeScript knowledge
  • CyberPod API access (or use mock mode for learning)

Part 1: Setup

Create Your Project

npx @cpod/cli create my-app
cd my-app
npm install

Install the SDK

npm install @cpod/sdk @cpod/react

Initialize the SDK

Create lib/sdk.ts:

import { CyberPod } from "@cpod/sdk";

// Create SDK instance
export const sdk = new CyberPod({
baseUrl: process.env.NEXT_PUBLIC_CPOD_BASE_URL!,
});

// Helper to set auth token after login
export function setAuthToken(token: string) {
sdk.setAccessToken(token);
}

Setup Provider

In your app/layout.tsx:

import "@cpod/react/styles.css";
import { ThemeProvider } from "@cpod/react";

export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<ThemeProvider>{children}</ThemeProvider>
</body>
</html>
);
}

Part 2: Authentication

SDK Functions

import { sdk } from "@/lib/sdk";

// Login with credentials
async function login(email: string, password: string) {
const tokens = await sdk.auth.authenticateUser({
username: email,
password: password,
});

// Store tokens
localStorage.setItem("access_token", tokens.access_token);
localStorage.setItem("refresh_token", tokens.refresh_token);

// Set for API calls
sdk.setAccessToken(tokens.access_token);

return tokens;
}

// Check current session
async function getSession() {
const session = await sdk.auth.getSession();
return session;
}

// Logout
async function logout() {
await sdk.auth.signOutUser();
localStorage.removeItem("access_token");
localStorage.removeItem("refresh_token");
}

// Refresh expired token
async function refreshToken() {
const refreshToken = localStorage.getItem("refresh_token");
const tokens = await sdk.auth.refreshToken({ refresh_token: refreshToken });
sdk.setAccessToken(tokens.access_token);
return tokens;
}

Simple Login Form

Live Editor
function LoginExample() {
  const [email, setEmail] = React.useState('');
  const [password, setPassword] = React.useState('');
  const [status, setStatus] = React.useState('');

  const handleLogin = async () => {
    setStatus('Logging in...');
    
    // In real app: const tokens = await sdk.auth.authenticateUser({...})
    await new Promise(r => setTimeout(r, 1000)); // Simulate API
    
    setStatus('✅ Logged in! Token stored.');
  };

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: '12px', maxWidth: '300px' }}>
      <input
        type="email"
        placeholder="Email"
        value={email}
        onChange={e => setEmail(e.target.value)}
        style={{ padding: '8px 12px', border: '1px solid #e2e8f0', borderRadius: '6px' }}
      />
      <input
        type="password"
        placeholder="Password"
        value={password}
        onChange={e => setPassword(e.target.value)}
        style={{ padding: '8px 12px', border: '1px solid #e2e8f0', borderRadius: '6px' }}
      />
      <button
        onClick={handleLogin}
        style={{ padding: '10px', background: '#8b5cf6', color: 'white', border: 'none', borderRadius: '6px', cursor: 'pointer' }}
      >
        Login
      </button>
      {status && <p style={{ margin: 0, fontSize: '14px', color: '#64748b' }}>{status}</p>}
    </div>
  );
}
Result
Loading...

Response Types

interface AuthTokens {
access_token: string;
refresh_token: string;
token_type: string;
expires_in: number;
}

interface Session {
user_id: string;
email: string;
name: string;
role: string;
permissions: string[];
}

Part 3: Users API

SDK Functions

import { sdk } from "@/lib/sdk";

// Get current user profile
async function getCurrentUser() {
const user = await sdk.users.getUser();
return user;
}

// Update user profile
async function updateProfile(data: { name?: string; avatar_url?: string }) {
const updated = await sdk.users.updateUser(data);
return updated;
}

// Get user activity logs
async function getUserActivity(startDate: string, endDate: string) {
const activity = await sdk.users.getUserActivity({
start_date: startDate,
end_date: endDate,
});
return activity;
}

Display User Profile

Live Editor
function UserProfileExample() {
  const [user, setUser] = React.useState(null);
  const [loading, setLoading] = React.useState(false);

  const fetchUser = async () => {
    setLoading(true);
    
    // In real app: const user = await sdk.users.getUser();
    await new Promise(r => setTimeout(r, 500));
    
    setUser({
      id: 'usr_abc123',
      name: 'John Doe',
      email: 'john@example.com',
      role: 'Developer',
      created_at: '2024-01-15',
    });
    setLoading(false);
  };

  return (
    <div style={{ maxWidth: '400px' }}>
      <button
        onClick={fetchUser}
        disabled={loading}
        style={{ padding: '8px 16px', background: '#8b5cf6', color: 'white', border: 'none', borderRadius: '6px', cursor: 'pointer', marginBottom: '16px' }}
      >
        {loading ? 'Loading...' : 'Fetch User (sdk.users.getUser)'}
      </button>
      
      {user && (
        <div style={{ padding: '16px', border: '1px solid #e2e8f0', borderRadius: '8px', background: '#f8fafc' }}>
          <p style={{ margin: '0 0 8px', fontWeight: 600 }}>{user.name}</p>
          <p style={{ margin: '0 0 4px', fontSize: '14px', color: '#64748b' }}>{user.email}</p>
          <p style={{ margin: '0 0 4px', fontSize: '14px', color: '#64748b' }}>Role: {user.role}</p>
          <p style={{ margin: 0, fontSize: '12px', color: '#94a3b8' }}>ID: {user.id}</p>
        </div>
      )}
    </div>
  );
}
Result
Loading...

Response Types

interface User {
id: string;
name: string;
email: string;
role: string;
avatar_url?: string;
created_at: string;
updated_at: string;
}

Part 4: Projects API

SDK Functions

import { sdk } from "@/lib/sdk";

// List all projects
async function listProjects(page = 1, limit = 20) {
const result = await sdk.projects.listProjects({ page, limit });
return result; // { projects: Project[], total: number }
}

// Create a project
async function createProject(name: string, description?: string) {
const project = await sdk.projects.createProject({
name,
description,
});
return project;
}

// Get project details
async function getProject(projectId: string) {
const project = await sdk.projects.getProject(projectId);
return project;
}

// Update project
async function updateProject(projectId: string, data: { name?: string; description?: string }) {
const updated = await sdk.projects.updateProject(projectId, data);
return updated;
}

// Delete project
async function deleteProject(projectId: string) {
await sdk.projects.deleteProject(projectId);
}

// Add files to project
async function addFilesToProject(projectId: string, fileIds: string[]) {
await sdk.projects.addFilesToProject(projectId, { file_ids: fileIds });
}

Projects List Example

Live Editor
function ProjectsExample() {
  const [projects, setProjects] = React.useState([]);
  const [loading, setLoading] = React.useState(false);
  const [newName, setNewName] = React.useState('');

  const fetchProjects = async () => {
    setLoading(true);
    
    // In real app: const { projects } = await sdk.projects.listProjects();
    await new Promise(r => setTimeout(r, 500));
    
    setProjects([
      { id: 'proj_1', name: 'Marketing Campaign', files_count: 12 },
      { id: 'proj_2', name: 'Q4 Report', files_count: 8 },
    ]);
    setLoading(false);
  };

  const createProject = async () => {
    if (!newName.trim()) return;
    
    // In real app: const project = await sdk.projects.createProject({ name: newName });
    setProjects([...projects, { id: `proj_${Date.now()}`, name: newName, files_count: 0 }]);
    setNewName('');
  };

  return (
    <div style={{ maxWidth: '400px' }}>
      <button
        onClick={fetchProjects}
        disabled={loading}
        style={{ padding: '8px 16px', background: '#8b5cf6', color: 'white', border: 'none', borderRadius: '6px', cursor: 'pointer', marginBottom: '16px' }}
      >
        {loading ? 'Loading...' : 'Fetch Projects (sdk.projects.listProjects)'}
      </button>

      <div style={{ display: 'flex', gap: '8px', marginBottom: '16px' }}>
        <input
          value={newName}
          onChange={e => setNewName(e.target.value)}
          placeholder="New project name"
          style={{ flex: 1, padding: '8px', border: '1px solid #e2e8f0', borderRadius: '6px' }}
        />
        <button
          onClick={createProject}
          style={{ padding: '8px 12px', background: '#22c55e', color: 'white', border: 'none', borderRadius: '6px', cursor: 'pointer' }}
        >
          Create
        </button>
      </div>

      {projects.length > 0 && (
        <div style={{ border: '1px solid #e2e8f0', borderRadius: '8px', overflow: 'hidden' }}>
          {projects.map((p, i) => (
            <div key={p.id} style={{ padding: '12px', borderBottom: i < projects.length - 1 ? '1px solid #e2e8f0' : 'none', background: '#fff' }}>
              <p style={{ margin: 0, fontWeight: 500 }}>{p.name}</p>
              <p style={{ margin: '4px 0 0', fontSize: '12px', color: '#64748b' }}>{p.files_count} files • {p.id}</p>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}
Result
Loading...

Response Types

interface Project {
id: string;
name: string;
description?: string;
status: "active" | "archived";
files_count: number;
created_at: string;
updated_at: string;
}

interface ListProjectsResponse {
projects: Project[];
total: number;
page: number;
limit: number;
}

Part 5: Chat Completions

The Chat API is the core of AI-powered features.

SDK Functions

import { sdk } from "@/lib/sdk";

// Simple chat completion
async function chat(message: string) {
const response = await sdk.chat.complete({
message,
});
return response.content;
}

// Chat with options
async function chatWithOptions(message: string, options?: {
temperature?: number;
maxTokens?: number;
expertId?: string;
projectId?: string;
}) {
const response = await sdk.chat.complete({
message,
temperature: options?.temperature ?? 0.7,
maxTokens: options?.maxTokens ?? 500,
expertId: options?.expertId,
projectId: options?.projectId,
});
return response;
}

// Streaming chat (for real-time responses)
async function* streamChat(message: string) {
const stream = sdk.chat.stream({ message });

for await (const chunk of stream) {
yield chunk.content;
}
}

// Multi-turn conversation
async function chatWithHistory(messages: Array<{ role: string; content: string }>) {
const response = await sdk.chat.complete({
messages,
});
return response;
}

Chat Example

Live Editor
function ChatExample() {
  const [message, setMessage] = React.useState('');
  const [response, setResponse] = React.useState('');
  const [loading, setLoading] = React.useState(false);

  const sendMessage = async () => {
    if (!message.trim()) return;
    
    setLoading(true);
    setResponse('');
    
    // In real app: const result = await sdk.chat.complete({ message });
    await new Promise(r => setTimeout(r, 1000));
    
    // Simulated response
    const replies = [
      "I'd be happy to help with that! Here's what I found...",
      "Great question! Let me explain...",
      "Based on my analysis, here are the key points...",
    ];
    setResponse(replies[Math.floor(Math.random() * replies.length)]);
    setLoading(false);
  };

  return (
    <div style={{ maxWidth: '500px' }}>
      <div style={{ display: 'flex', gap: '8px', marginBottom: '16px' }}>
        <input
          value={message}
          onChange={e => setMessage(e.target.value)}
          placeholder="Ask something..."
          onKeyDown={e => e.key === 'Enter' && sendMessage()}
          style={{ flex: 1, padding: '10px', border: '1px solid #e2e8f0', borderRadius: '6px' }}
        />
        <button
          onClick={sendMessage}
          disabled={loading}
          style={{ padding: '10px 16px', background: '#8b5cf6', color: 'white', border: 'none', borderRadius: '6px', cursor: 'pointer' }}
        >
          {loading ? '...' : 'Send'}
        </button>
      </div>

      {response && (
        <div style={{ padding: '16px', background: '#f8fafc', border: '1px solid #e2e8f0', borderRadius: '8px' }}>
          <p style={{ margin: 0, fontSize: '12px', color: '#64748b', marginBottom: '8px' }}>sdk.chat.complete() response:</p>
          <p style={{ margin: 0 }}>{response}</p>
        </div>
      )}
    </div>
  );
}
Result
Loading...

Streaming Example

// Using streaming in a React component
function StreamingChat() {
const [content, setContent] = useState('');

async function handleStream(message: string) {
setContent('');

const stream = sdk.chat.stream({ message });

for await (const chunk of stream) {
setContent(prev => prev + chunk.content);
}
}
}

Response Types

interface ChatRequest {
message?: string;
messages?: ChatMessage[];
expertId?: string;
projectId?: string;
temperature?: number;
maxTokens?: number;
}

interface ChatMessage {
role: "system" | "user" | "assistant";
content: string;
}

interface ChatResponse {
id: string;
content: string;
role: "assistant";
model: string;
usage?: {
promptTokens: number;
completionTokens: number;
totalTokens: number;
};
}

Part 6: Files API

SDK Functions

import { sdk } from "@/lib/sdk";

// List files
async function listFiles(folderId?: string) {
const files = await sdk.files.listFiles({ folder_id: folderId });
return files;
}

// Upload a file
async function uploadFile(file: File, folderId?: string) {
const result = await sdk.files.uploadFile({
file,
folder_id: folderId,
});
return result;
}

// Download a file
async function downloadFile(fileId: string) {
const blob = await sdk.files.downloadFile(fileId);
return blob;
}

// Search files
async function searchFiles(query: string) {
const results = await sdk.files.searchFiles({ query });
return results;
}

// Delete a file
async function deleteFile(fileId: string) {
await sdk.files.deleteFile(fileId);
}

// Share a file
async function shareFile(fileId: string, userIds: string[]) {
await sdk.files.shareFile(fileId, { user_ids: userIds });
}

Files List Example

Live Editor
function FilesExample() {
  const [files, setFiles] = React.useState([]);
  const [loading, setLoading] = React.useState(false);

  const fetchFiles = async () => {
    setLoading(true);
    
    // In real app: const files = await sdk.files.listFiles();
    await new Promise(r => setTimeout(r, 500));
    
    setFiles([
      { id: 'file_1', name: 'report.pdf', size: '2.4 MB', type: 'application/pdf' },
      { id: 'file_2', name: 'data.csv', size: '156 KB', type: 'text/csv' },
      { id: 'file_3', name: 'presentation.pptx', size: '5.1 MB', type: 'application/vnd.ms-powerpoint' },
    ]);
    setLoading(false);
  };

  return (
    <div style={{ maxWidth: '400px' }}>
      <button
        onClick={fetchFiles}
        disabled={loading}
        style={{ padding: '8px 16px', background: '#8b5cf6', color: 'white', border: 'none', borderRadius: '6px', cursor: 'pointer', marginBottom: '16px' }}
      >
        {loading ? 'Loading...' : 'Fetch Files (sdk.files.listFiles)'}
      </button>

      {files.length > 0 && (
        <div style={{ border: '1px solid #e2e8f0', borderRadius: '8px', overflow: 'hidden' }}>
          {files.map((f, i) => (
            <div key={f.id} style={{ padding: '12px', borderBottom: i < files.length - 1 ? '1px solid #e2e8f0' : 'none', background: '#fff', display: 'flex', justifyContent: 'space-between' }}>
              <div>
                <p style={{ margin: 0, fontWeight: 500 }}>{f.name}</p>
                <p style={{ margin: '2px 0 0', fontSize: '12px', color: '#64748b' }}>{f.size}</p>
              </div>
              <button style={{ padding: '4px 8px', fontSize: '12px', border: '1px solid #e2e8f0', borderRadius: '4px', background: '#fff', cursor: 'pointer' }}>
                Download
              </button>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}
Result
Loading...

Response Types

interface File {
id: string;
name: string;
size: number;
mime_type: string;
folder_id?: string;
created_at: string;
updated_at: string;
}

Part 7: Notifications API

SDK Functions

import { sdk } from "@/lib/sdk";

// Get notifications
async function getNotifications(page = 1, limit = 20) {
const result = await sdk.notifications.getNotifications({ page, limit });
return result;
}

// Mark as read
async function markAsRead(notificationId: string) {
await sdk.notifications.markNotificationRead(notificationId, true);
}

// Archive notifications
async function archiveNotifications(ids: string[]) {
await sdk.notifications.archiveNotifications(ids);
}

// Delete notification
async function deleteNotification(notificationId: string) {
await sdk.notifications.deleteNotification(notificationId);
}

// Clear all notifications
async function clearAll() {
await sdk.notifications.clearAllNotifications({ scope: "all" });
}

Notifications Example

Live Editor
function NotificationsExample() {
  const [notifications, setNotifications] = React.useState([]);
  const [loading, setLoading] = React.useState(false);

  const fetchNotifications = async () => {
    setLoading(true);
    
    // In real app: const { notifications } = await sdk.notifications.getNotifications();
    await new Promise(r => setTimeout(r, 500));
    
    setNotifications([
      { id: '1', title: 'New comment', message: 'Alex commented on your file', is_read: false },
      { id: '2', title: 'Project shared', message: 'Sarah shared "Q4 Report"', is_read: false },
      { id: '3', title: 'Task completed', message: 'Review task marked done', is_read: true },
    ]);
    setLoading(false);
  };

  const markRead = (id) => {
    // In real app: await sdk.notifications.markNotificationRead(id, true);
    setNotifications(notifications.map(n => n.id === id ? { ...n, is_read: true } : n));
  };

  return (
    <div style={{ maxWidth: '400px' }}>
      <button
        onClick={fetchNotifications}
        disabled={loading}
        style={{ padding: '8px 16px', background: '#8b5cf6', color: 'white', border: 'none', borderRadius: '6px', cursor: 'pointer', marginBottom: '16px' }}
      >
        {loading ? 'Loading...' : 'Fetch Notifications'}
      </button>

      {notifications.length > 0 && (
        <div style={{ border: '1px solid #e2e8f0', borderRadius: '8px', overflow: 'hidden' }}>
          {notifications.map((n, i) => (
            <div
              key={n.id}
              onClick={() => markRead(n.id)}
              style={{
                padding: '12px',
                borderBottom: i < notifications.length - 1 ? '1px solid #e2e8f0' : 'none',
                background: n.is_read ? '#fff' : '#faf5ff',
                cursor: 'pointer',
              }}
            >
              <p style={{ margin: 0, fontWeight: n.is_read ? 400 : 600, fontSize: '14px' }}>{n.title}</p>
              <p style={{ margin: '4px 0 0', fontSize: '13px', color: '#64748b' }}>{n.message}</p>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}
Result
Loading...

Response Types

interface Notification {
id: string;
title: string;
message: string;
type: string;
is_read: boolean;
created_at: string;
}

interface GetNotificationsResponse {
notifications: Notification[];
total: number;
page: number;
limit: number;
}

Part 8: Error Handling

Best Practices

import { sdk } from "@/lib/sdk";

// Wrap API calls with error handling
async function safeApiCall<T>(fn: () => Promise<T>): Promise<{ data?: T; error?: string }> {
try {
const data = await fn();
return { data };
} catch (error) {
if (error.status === 401) {
// Token expired, try refresh
await refreshToken();
const data = await fn();
return { data };
}

if (error.status === 403) {
return { error: "You don't have permission for this action" };
}

if (error.status === 404) {
return { error: "Resource not found" };
}

return { error: error.message || "Something went wrong" };
}
}

// Usage
const { data: user, error } = await safeApiCall(() => sdk.users.getUser());

if (error) {
console.error(error);
} else {
console.log(user);
}

React Hook Pattern

function useApi<T>(fetcher: () => Promise<T>) {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);

const execute = async () => {
setLoading(true);
setError(null);

try {
const result = await fetcher();
setData(result);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};

return { data, loading, error, execute };
}

// Usage
function MyComponent() {
const { data: user, loading, error, execute } = useApi(() => sdk.users.getUser());

useEffect(() => {
execute();
}, []);
}

Quick Reference

All SDK Modules

ModuleCommon Functions
sdk.authauthenticateUser, getSession, signOutUser, refreshToken
sdk.usersgetUser, updateUser, getUserActivity
sdk.projectslistProjects, createProject, getProject, deleteProject
sdk.fileslistFiles, uploadFile, downloadFile, searchFiles
sdk.folderslistFolders, createFolder, deleteFolder
sdk.chatcomplete, stream
sdk.notificationsgetNotifications, markNotificationRead, archiveNotifications
sdk.storageget, set, delete, list
sdk.expertslistExperts, getExpert
sdk.taskslistTasks, getTask, cancelTask

Import Pattern

// Option 1: Class-based (recommended for most cases)
import { CyberPod } from "@cpod/sdk";
const sdk = new CyberPod({ baseUrl: "..." });

// Option 2: Global functions (simpler syntax)
import { initSDK, chat, users, projects } from "@cpod/sdk";
initSDK({ baseUrl: "..." });

const user = await users.getUser();
const response = await chat.complete({ message: "Hello" });

Next Steps