Skip to main content

React Hooks

All hooks are provided by @ainative/react-sdk and require the AINativeProvider to be present in the component tree.

npm install @ainative/react-sdk

Requires: React 18+


Setup — AINativeProvider​

Wrap your application (or the subtree that uses AINative hooks) with AINativeProvider. This configures the API key and base URL for all child hooks.

import { AINativeProvider } from '@ainative/react-sdk';

export function App({ children }: { children: React.ReactNode }) {
return (
<AINativeProvider
config={{
apiKey: process.env.NEXT_PUBLIC_AINATIVE_API_KEY!,
// baseUrl defaults to 'https://api.ainative.studio/api/v1'
}}
>
{children}
</AINativeProvider>
);
}

AINativeProvider props​

NameTypeDefaultDescription
configAINativeConfigrequiredAPI configuration
config.apiKeystringrequiredYour AINative JWT token or API key
config.baseUrlstring'https://api.ainative.studio/api/v1'Override the API base URL
childrenReactNoderequiredComponent subtree

useAINative​

Access the underlying API client configuration from any component inside the provider. Useful for building custom hooks or performing raw fetch calls with the configured credentials.

Signature​

function useAINative(): AINativeClient

Return type​

interface AINativeClient {
config: AINativeConfig; // { apiKey, baseUrl? }
baseUrl: string; // Resolved base URL
}

Example​

import { useAINative } from '@ainative/react-sdk';

export function DebugPanel() {
const client = useAINative();

return (
<pre className="text-xs">
{JSON.stringify({ baseUrl: client.baseUrl }, null, 2)}
</pre>
);
}

useChat​

Send chat completion requests and manage conversation state. All messages in the conversation are stored in the hook's local state; call sendMessage with the full message array (including history) to maintain context across turns.

Signature​

function useChat(options?: UseChatOptions): {
messages: Message[];
isLoading: boolean;
error: AINativeError | null;
response: ChatCompletionResponse | null;
sendMessage: (messages: Message[]) => Promise<ChatCompletionResponse | null>;
reset: () => void;
}

Options​

NameTypeDefaultDescription
modelstring—Preferred model identifier (e.g. 'meta-llama/llama-3.3-70b-instruct')
temperaturenumber—Sampling temperature (0–2)
max_tokensnumber—Maximum tokens to generate
onError(error: AINativeError) => void—Callback invoked on request failure
onSuccess(response: ChatCompletionResponse) => void—Callback invoked on successful completion

Return values​

NameTypeDescription
messagesMessage[]Current conversation messages (user + assistant turns)
isLoadingbooleantrue while a request is in flight
errorAINativeError | nullLast error, or null if none
responseChatCompletionResponse | nullRaw response from the last successful request
sendMessage(messages: Message[]) => Promise<ChatCompletionResponse | null>Send a new completion request. Pass the full message array including history
reset() => voidClear all messages and reset state to the initial empty state

Example​

import { useState } from 'react';
import { useChat } from '@ainative/react-sdk';
import type { Message } from '@ainative/react-sdk';

export function ChatBox() {
const { messages, sendMessage, isLoading, error, reset } = useChat({
model: 'meta-llama/llama-3.3-70b-instruct',
temperature: 0.7,
onError: (err) => console.error('Chat error:', err.message),
});
const [input, setInput] = useState('');

const handleSend = async () => {
if (!input.trim() || isLoading) return;

const newMessages: Message[] = [
...messages,
{ role: 'user', content: input },
];
setInput('');
await sendMessage(newMessages);
};

return (
<div>
<div className="messages">
{messages.map((msg, i) => (
<div key={i} className={`message ${msg.role}`}>
<strong>{msg.role}:</strong> {msg.content}
</div>
))}
</div>

{error && <p className="error">{error.message}</p>}

<div className="input-row">
<input
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && handleSend()}
disabled={isLoading}
placeholder="Ask anything…"
/>
<button onClick={handleSend} disabled={isLoading}>
{isLoading ? 'Sending…' : 'Send'}
</button>
<button onClick={reset}>Clear</button>
</div>
</div>
);
}

useAgent​

Manage agent registrations — create, list, retrieve, and delete agent records via the AINative Agent API. Fetches the agent list automatically on mount.

Signature​

function useAgent(): UseAgentReturn

Return type​

interface UseAgentReturn {
agents: AgentRegistration[];
isLoading: boolean;
error: AINativeError | null;
create: (config: CreateAgentRequest) => Promise<AgentRegistration | null>;
get: (id: string) => Promise<AgentRegistration | null>;
list: () => Promise<AgentRegistration[]>;
remove: (id: string) => Promise<boolean>;
refetch: () => Promise<void>;
}

Key types​

interface AgentRegistration {
id: string;
name: string;
agent_type: string;
model?: string;
capabilities: string[];
oversight_level: string;
status: string;
created_at: string;
}

interface CreateAgentRequest {
name: string;
agent_type: string;
model?: string;
capabilities: string[];
oversight_level?: string;
}

Methods​

NameSignatureDescription
create(config: CreateAgentRequest) => Promise<AgentRegistration | null>Register a new agent. Adds it to the local list on success
get(id: string) => Promise<AgentRegistration | null>Fetch a single agent by ID
list() => Promise<AgentRegistration[]>Refresh the agent list from the API and update local state
remove(id: string) => Promise<boolean>Delete an agent. Removes it from local state on success
refetch() => Promise<void>Re-run the initial load (same as list but returns void)

Example​

import { useAgent } from '@ainative/react-sdk';

export function AgentManager() {
const { agents, isLoading, error, create, remove } = useAgent();

const handleCreate = async () => {
await create({
name: 'Summarizer',
agent_type: 'assistant',
capabilities: ['summarize', 'extract'],
oversight_level: 'medium',
});
};

if (isLoading) return <p>Loading agents…</p>;
if (error) return <p>Error: {error.message}</p>;

return (
<div>
<button onClick={handleCreate}>Register Agent</button>
<ul>
{agents.map((agent) => (
<li key={agent.id}>
<strong>{agent.name}</strong> — {agent.agent_type}
<span className="status">{agent.status}</span>
<button onClick={() => remove(agent.id)}>Delete</button>
</li>
))}
</ul>
</div>
);
}

useCredits​

Fetch and display a user's credit balance. Runs once on mount; call refetch to refresh.

Signature​

function useCredits(): UseCreditsReturn

Return type​

interface UseCreditsReturn {
balance: CreditBalance | null;
isLoading: boolean;
error: AINativeError | null;
refetch: () => Promise<void>;
}

interface CreditBalance {
total_credits: number;
used_credits: number;
remaining_credits: number;
plan: string;
period_start: string;
period_end: string | null;
usage_percentage: number;
}

Example​

import { useCredits } from '@ainative/react-sdk';

export function CreditsWidget() {
const { balance, isLoading, error, refetch } = useCredits();

if (isLoading) return <span>Loading…</span>;
if (error) return <span>Failed to load credits</span>;
if (!balance) return null;

const pct = balance.usage_percentage.toFixed(0);

return (
<div className="credits-widget">
<p>
{balance.remaining_credits.toLocaleString()} credits remaining
({pct}% used)
</p>
<p className="plan">Plan: {balance.plan}</p>
<button onClick={refetch}>Refresh</button>
</div>
);
}

useMemory​

Store, recall, and delete memories via the ZeroMemory API (/public/memory/v2). When an entityId is provided the hook auto-loads memories for that entity on mount.

Signature​

function useMemory(entityId?: string): UseMemoryReturn

Parameters​

NameTypeDescription
entityIdstring (optional)Scope all memory operations to this entity ID. When provided, memories are fetched on mount

Return type​

interface UseMemoryReturn {
memories: Memory[];
isLoading: boolean;
error: AINativeError | null;
remember: (content: string, options?: RememberOptions) => Promise<Memory | null>;
recall: (query: string, options?: RecallOptions) => Promise<Memory[]>;
forget: (memoryId: string) => Promise<boolean>;
refetch: () => Promise<void>;
}

Key types​

interface Memory {
id: string;
content: string;
memory_type: string;
importance: number; // 0–1 importance score
tags: string[];
entity_id?: string;
metadata: Record<string, unknown>;
created_at: string;
score?: number; // Similarity score returned by recall
}

interface RememberOptions {
entity_id?: string; // Override the hook-level entityId
memory_type?: string; // e.g. 'episodic', 'semantic', 'procedural'
importance?: number; // 0–1
tags?: string[];
metadata?: Record<string, unknown>;
}

interface RecallOptions {
entity_id?: string; // Override the hook-level entityId
layer?: string; // Memory layer filter
limit?: number; // Maximum results to return
}

Methods​

NameSignatureDescription
remember(content: string, options?: RememberOptions) => Promise<Memory | null>Store a new memory. Prepends to local list on success
recall(query: string, options?: RecallOptions) => Promise<Memory[]>Semantic search over stored memories. Updates local list with results
forget(memoryId: string) => Promise<boolean>Delete a memory by ID. Removes it from local list on success
refetch() => Promise<void>Re-run the initial entity load (only active when entityId was provided)

Example​

import { useMemory } from '@ainative/react-sdk';

export function UserMemoryPanel({ userId }: { userId: string }) {
const { memories, remember, recall, forget, isLoading, error } = useMemory(userId);

const handleSave = () =>
remember('User prefers concise responses without preamble', {
memory_type: 'preference',
importance: 0.8,
tags: ['communication', 'style'],
});

const handleSearch = () =>
recall('communication preferences', { limit: 10 });

if (isLoading) return <p>Loading memories…</p>;

return (
<div>
<button onClick={handleSave}>Save preference</button>
<button onClick={handleSearch}>Search</button>

{error && <p className="error">{error.message}</p>}

<ul>
{memories.map((m) => (
<li key={m.id}>
{m.content}
{m.score !== undefined && (
<small> (score: {m.score.toFixed(3)})</small>
)}
<button onClick={() => forget(m.id)}>Forget</button>
</li>
))}
</ul>
</div>
);
}

useThread​

Create and manage conversation threads. Fetches the thread list on mount. Supports full CRUD, message appending, semantic/keyword search, and thread forking.

Signature​

function useThread(): UseThreadReturn

Return type​

interface UseThreadReturn {
threads: Thread[];
isLoading: boolean;
error: AINativeError | null;
create: (title?: string, agentTypes?: string[]) => Promise<Thread | null>;
get: (threadId: string) => Promise<ThreadWithMessages | null>;
list: (opts?: { limit?: number; status?: string; search?: string }) => Promise<Thread[]>;
appendMessage: (
threadId: string,
message: { role: string; content: string }
) => Promise<ThreadMessage | null>;
search: (query: string, mode?: SearchMode) => Promise<Thread[]>;
fork: (threadId: string, fromMessageId: string, title?: string) => Promise<Thread | null>;
remove: (threadId: string) => Promise<boolean>;
refetch: () => Promise<void>;
}

Key types​

type SearchMode = 'semantic' | 'keyword' | 'hybrid';

interface Thread {
id: string;
title: string | null;
agent_types: string[];
model: string | null;
status: string;
message_count: number;
last_message_at: string | null;
metadata: Record<string, unknown>;
created_at: string;
}

interface ThreadMessage {
id: string;
thread_id: string;
role: string;
content: string | null;
tool_calls: unknown[] | null;
tokens_used: number | null;
created_at: string;
}

interface ThreadWithMessages extends Thread {
messages: ThreadMessage[];
}

Methods​

NameSignatureDescription
create(title?, agentTypes?) => Promise<Thread | null>Create a new thread. Prepends to local list
get(threadId) => Promise<ThreadWithMessages | null>Fetch a thread with its full message history
list(opts?) => Promise<Thread[]>Refresh thread list with optional filters
appendMessage(threadId, message) => Promise<ThreadMessage | null>Add a message to a thread. Updates message_count in local state
search(query, mode?) => Promise<Thread[]>Search threads. Defaults to 'semantic' mode
fork(threadId, fromMessageId, title?) => Promise<Thread | null>Fork a thread from a specific message, creating a new branch
remove(threadId) => Promise<boolean>Delete a thread and remove it from local state
refetch() => Promise<void>Reload the thread list

Example​

import { useState } from 'react';
import { useThread } from '@ainative/react-sdk';

export function ThreadSidebar() {
const { threads, isLoading, error, create, get, remove, search } =
useThread();
const [query, setQuery] = useState('');

const handleNew = () => create('New conversation');

const handleSearch = async () => {
if (query.trim()) await search(query, 'hybrid');
};

const handleFetchMessages = async (threadId: string) => {
const full = await get(threadId);
if (full) console.log(full.messages);
};

if (isLoading) return <p>Loading threads…</p>;
if (error) return <p>Error: {error.message}</p>;

return (
<aside>
<button onClick={handleNew}>New thread</button>

<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search threads…"
onKeyDown={(e) => e.key === 'Enter' && handleSearch()}
/>

<ul>
{threads.map((t) => (
<li key={t.id}>
<button onClick={() => handleFetchMessages(t.id)}>
{t.title ?? 'Untitled'} ({t.message_count} msgs)
</button>
<button onClick={() => remove(t.id)}>Delete</button>
</li>
))}
</ul>
</aside>
);
}

useTask​

Submit tasks to the AINative agent swarm and track their progress. Automatically polls the active task until it reaches a terminal state (completed, failed, or cancelled).

Signature​

function useTask(options?: UseTaskOptions): UseTaskReturn

Options​

NameTypeDefaultDescription
pollIntervalnumber2000Polling interval in milliseconds while a task is active

Return type​

interface UseTaskReturn {
tasks: SwarmTask[];
status: SwarmTaskStatus | null;
result: Record<string, unknown> | null;
error: AINativeError | null;
isLoading: boolean;
submit: (
description: string,
agentTypes?: string[],
config?: Record<string, unknown>
) => Promise<SwarmTask | null>;
poll: (taskId: string) => Promise<SwarmTask | null>;
cancel: (taskId: string) => Promise<boolean>;
listTasks: (opts?: {
status?: string;
limit?: number;
offset?: number;
}) => Promise<SwarmTask[]>;
}

Key types​

type SwarmTaskStatus = 'queued' | 'running' | 'completed' | 'failed' | 'cancelled';

interface SwarmTask {
task_id: string;
status: SwarmTaskStatus;
description: string;
agent_types: string[];
config: Record<string, unknown>;
result: Record<string, unknown> | null;
agents_used: string[];
created_at: string;
updated_at: string;
}

Methods​

NameSignatureDescription
submit(description, agentTypes?, config?) => Promise<SwarmTask | null>Submit a task to the swarm and begin auto-polling
poll(taskId) => Promise<SwarmTask | null>Manually poll a specific task for its current status
cancel(taskId) => Promise<boolean>Cancel an active task and stop polling
listTasks(opts?) => Promise<SwarmTask[]>Fetch the task list with optional status filter, limit, and offset

Example​

import { useTask } from '@ainative/react-sdk';

export function TaskRunner() {
const { submit, status, result, error, isLoading, cancel, tasks } = useTask({
pollInterval: 3000,
});

const handleRun = async () => {
const task = await submit(
'Analyze Q1 customer feedback and summarize key themes',
['analyst', 'summarizer'],
{ format: 'bullet_points', max_themes: 5 }
);
if (task) console.log('Task created:', task.task_id);
};

const handleCancel = () => {
const activeTask = tasks.find(
(t) => t.status === 'running' || t.status === 'queued'
);
if (activeTask) cancel(activeTask.task_id);
};

return (
<div>
<button onClick={handleRun} disabled={isLoading}>
Run Analysis
</button>
{isLoading && (
<button onClick={handleCancel}>Cancel</button>
)}

{status && (
<p>
Status: <strong>{status}</strong>
</p>
)}

{result && (
<pre>{JSON.stringify(result, null, 2)}</pre>
)}

{error && <p className="error">{error.message}</p>}
</div>
);
}

Next.js usage​

All hooks are available from @ainative/next-sdk/client when using the Next.js SDK.

// app/layout.tsx
import { AINativeProvider } from '@ainative/next-sdk/client';
import { getApiKey } from '@ainative/next-sdk/server';

export default async function RootLayout({ children }) {
const apiKey = await getApiKey();
return (
<html>
<body>
<AINativeProvider config={{ apiKey }}>
{children}
</AINativeProvider>
</body>
</html>
);
}
// app/components/Chat.tsx
'use client';
import { useChat } from '@ainative/next-sdk/client';

The AI Kit React SDK also supports Next.js server-side patterns including createServerClient, withAuth, and streaming Server Components.


Error handling​

All hooks expose an error field of type AINativeError:

interface AINativeError {
message: string;
status?: number; // HTTP status code
code?: string; // API error code
}

Common status codes:

StatusMeaning
401Invalid or expired API key
402Insufficient credits
422Validation error — check request parameters
429Rate limit exceeded
503Service temporarily unavailable