React Integration

Integrate the Fig1 AI SDK into your React or Next.js application.

Installation

No npm package required—the SDK is a REST API. Use fetch or any HTTP client.

For TypeScript, copy the types from the Types Reference.

API Client

Create a reusable client:

// lib/fig1-client.ts
const API_BASE = 'https://app.fig1.ai/api/sdk';

export class Fig1Client {
  private apiKey: string;

  constructor(apiKey: string) {
    this.apiKey = apiKey;
  }

  private async request<T>(
    endpoint: string,
    options: RequestInit = {}
  ): Promise<T> {
    const response = await fetch(`${API_BASE}${endpoint}`, {
      ...options,
      headers: {
        'Content-Type': 'application/json',
        'X-Fig1-API-Key': this.apiKey,
        ...options.headers,
      },
    });

    const data = await response.json();

    if (!data.success) {
      throw new Error(data.error || 'API request failed');
    }

    return data.data;
  }

  async chat(message: string, sessionId?: string, options?: {
    personaId?: string;
    preferences?: Record<string, any>;
    context?: Record<string, any>;
  }) {
    return this.request<{
      message: string;
      sessionId: string;
      actions?: any[];
      metadata: { model: string; tokensUsed: number };
    }>('/agent/chat', {
      method: 'POST',
      body: JSON.stringify({ message, sessionId, ...options }),
    });
  }

  async search(query: string, options?: {
    searchType?: 'hybrid' | 'standard' | 'expanded';
    maxResults?: number;
  }) {
    return this.request<{
      results: any[];
      totalResults: number;
    }>('/rag/search', {
      method: 'POST',
      body: JSON.stringify({ query, ...options }),
    });
  }
}

// Singleton instance
export const fig1 = new Fig1Client(process.env.NEXT_PUBLIC_FIG1_API_KEY!);

useAgent Hook

// hooks/useAgent.ts
import { useState, useCallback } from 'react';
import { fig1 } from '@/lib/fig1-client';

interface Message {
  role: 'user' | 'assistant';
  content: string;
}

export function useAgent(personaId?: string) {
  const [messages, setMessages] = useState<Message[]>([]);
  const [sessionId, setSessionId] = useState<string | null>(null);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const sendMessage = useCallback(async (
    text: string,
    preferences?: Record<string, any>
  ) => {
    setIsLoading(true);
    setError(null);

    // Add user message immediately
    setMessages(prev => [...prev, { role: 'user', content: text }]);

    try {
      const response = await fig1.chat(text, sessionId || undefined, {
        personaId,
        preferences,
      });

      setSessionId(response.sessionId);
      setMessages(prev => [
        ...prev,
        { role: 'assistant', content: response.message }
      ]);

      return response;
    } catch (err) {
      const message = err instanceof Error ? err.message : 'Failed to send';
      setError(message);
      setMessages(prev => prev.slice(0, -1)); // Remove optimistic message
      throw err;
    } finally {
      setIsLoading(false);
    }
  }, [sessionId, personaId]);

  const clearChat = useCallback(() => {
    setMessages([]);
    setSessionId(null);
    setError(null);
  }, []);

  return { messages, sendMessage, clearChat, isLoading, error, sessionId };
}

useAgentActions Hook

Handle agent-triggered actions like navigation:

// hooks/useAgentActions.ts
import { useCallback } from 'react';
import { useRouter } from 'next/navigation';

interface ClientAction {
  type: string;
  payload: {
    route?: string;
    url?: string;
    productId?: string;
    modalId?: string;
  };
  immediate?: boolean;
}

export function useAgentActions(options: {
  onNavigate?: (route: string) => void;
  onShowProduct?: (productId: string) => void;
  onOpenModal?: (modalId: string) => void;
} = {}) {
  const router = useRouter();

  const handleActions = useCallback((actions: ClientAction[]) => {
    for (const action of actions) {
      if (action.immediate === false) continue;

      switch (action.type) {
        case 'navigate':
          if (action.payload.route) {
            options.onNavigate?.(action.payload.route) ?? router.push(action.payload.route);
          }
          break;

        case 'open_url':
          if (action.payload.url) {
            window.open(action.payload.url, '_blank');
          }
          break;

        case 'show_product':
          if (action.payload.productId) {
            options.onShowProduct?.(action.payload.productId);
          }
          break;

        case 'open_modal':
          if (action.payload.modalId) {
            options.onOpenModal?.(action.payload.modalId);
          }
          break;
      }
    }
  }, [router, options]);

  return { handleActions };
}

Chat Component

// components/Chat.tsx
'use client';

import { useState } from 'react';
import { useAgent } from '@/hooks/useAgent';

export function Chat({ personaId }: { personaId?: string }) {
  const [input, setInput] = useState('');
  const { messages, sendMessage, isLoading, error } = useAgent(personaId);

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    if (!input.trim() || isLoading) return;

    const message = input;
    setInput('');
    await sendMessage(message);
  };

  return (
    <div className="flex flex-col h-[500px]">
      {/* Messages */}
      <div className="flex-1 overflow-y-auto p-4 space-y-4">
        {messages.map((msg, i) => (
          <div
            key={i}
            className={`flex ${msg.role === 'user' ? 'justify-end' : 'justify-start'}`}
          >
            <div
              className={`max-w-[80%] rounded-lg p-3 ${
                msg.role === 'user'
                  ? 'bg-blue-500 text-white'
                  : 'bg-gray-100 text-gray-900'
              }`}
            >
              {msg.content}
            </div>
          </div>
        ))}
        {isLoading && (
          <div className="flex justify-start">
            <div className="bg-gray-100 rounded-lg p-3">
              <span className="animate-pulse">Thinking...</span>
            </div>
          </div>
        )}
      </div>

      {error && <div className="px-4 py-2 text-red-500 text-sm">{error}</div>}

      {/* Input */}
      <form onSubmit={handleSubmit} className="p-4 border-t">
        <div className="flex gap-2">
          <input
            type="text"
            value={input}
            onChange={(e) => setInput(e.target.value)}
            placeholder="Type your message..."
            className="flex-1 border rounded-lg px-4 py-2"
            disabled={isLoading}
          />
          <button
            type="submit"
            disabled={isLoading || !input.trim()}
            className="bg-blue-500 text-white px-4 py-2 rounded-lg disabled:opacity-50"
          >
            Send
          </button>
        </div>
      </form>
    </div>
  );
}

Next.js API Route (Recommended)

For security, proxy requests through your backend:

// app/api/chat/route.ts
import { NextRequest, NextResponse } from 'next/server';

export async function POST(request: NextRequest) {
  const body = await request.json();

  const response = await fetch('https://app.fig1.ai/api/sdk/agent/chat', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Fig1-API-Key': process.env.FIG1_API_KEY!, // Server-side only
    },
    body: JSON.stringify(body),
  });

  const data = await response.json();
  return NextResponse.json(data);
}

Then update your client to use your API route:

// Use your backend instead of Fig1 directly
const response = await fetch('/api/chat', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ message, sessionId }),
});

Environment Variables

# .env.local
FIG1_API_KEY=fig1_sdk_your_api_key

# Only if calling from client (not recommended)
NEXT_PUBLIC_FIG1_API_KEY=fig1_sdk_your_api_key