SDK & TypeScript Types
This guide provides TypeScript types and a reference client implementation for integrating with the Spritz API.
SDK Status
An official SDK is planned for a future release. Track progress on the GitHub repository. In the meantime, use the types and patterns below for type-safe integration with the Spritz API.
Installation
No package installation required. Copy the types and utilities you need into your project.
Core Types
API Response Types
// types/spritz-api.ts
/**
* Standard API response wrapper
*/
export interface ApiResponse<T> {
success: boolean;
data?: T;
error?: ApiError;
}
export interface ApiError {
code: string;
message: string;
retryAfter?: number;
}
/**
* Pagination response
*/
export interface PaginatedResponse<T> {
items: T[];
total: number;
limit: number;
offset: number;
hasMore: boolean;
}
User Types
// types/user.ts
export interface User {
id: string;
wallet_address: string;
username?: string;
created_at: string;
last_login?: string;
is_admin: boolean;
beta_access: boolean;
}
export interface UserProfile {
wallet_address: string;
username?: string;
bio?: string;
status?: string;
avatar_url?: string;
ens_name?: string;
social_links?: SocialLinks;
public_profile_enabled: boolean;
}
export interface SocialLinks {
twitter?: string;
farcaster?: string;
lens?: string;
discord?: string;
github?: string;
}
export type AuthMethod =
| 'wallet'
| 'passkey'
| 'email'
| 'worldid'
| 'alien'
| 'solana';
export interface Session {
address: string;
authMethod: AuthMethod;
chainId?: number;
passkeyCredentialId?: string;
iat: number;
exp: number;
}
Agent Types
// types/agent.ts
export interface Agent {
id: string;
owner_address: string;
name: string;
personality: string;
system_instructions?: string;
model: GeminiModel;
avatar_emoji: string;
avatar_url?: string;
visibility: AgentVisibility;
web_search_enabled: boolean;
use_knowledge_base: boolean;
message_count: number;
tags: string[];
x402_enabled: boolean;
x402_price_cents?: number;
x402_network?: string;
x402_wallet_address?: string;
mcp_servers: McpServer[];
api_tools: ApiTool[];
created_at: string;
updated_at: string;
}
export type AgentVisibility = 'private' | 'friends' | 'public';
// Currently only gemini-2.0-flash is supported
export type GeminiModel = 'gemini-2.0-flash';
export interface McpServer {
name: string;
url: string;
headers?: Record<string, string>;
}
export interface ApiTool {
name: string;
description: string;
baseUrl: string;
endpoints: ApiEndpoint[];
headers?: Record<string, string>;
}
export interface ApiEndpoint {
path: string;
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
parameters?: Record<string, ParameterDefinition>;
}
export interface ParameterDefinition {
type: 'string' | 'number' | 'boolean';
required?: boolean;
default?: string | number | boolean;
description?: string;
}
export interface AgentKnowledge {
id: string;
agent_id: string;
title: string;
url: string;
content_type: 'webpage' | 'github' | 'docs';
status: 'pending' | 'processing' | 'indexed' | 'failed';
error_message?: string;
chunk_count: number;
created_at: string;
indexed_at?: string;
}
export interface ChatMessage {
id: string;
role: 'user' | 'assistant';
content: string;
created_at: string;
}
export interface ChatResponse {
success: boolean;
sessionId: string;
message: string;
agent: {
id: string;
name: string;
emoji: string;
};
usage?: {
promptTokens: number;
completionTokens: number;
totalTokens: number;
};
}
export interface CreateAgentRequest {
userAddress: string;
name: string;
personality: string;
system_instructions?: string;
model?: GeminiModel;
avatar_emoji?: string;
visibility?: AgentVisibility;
web_search_enabled?: boolean;
use_knowledge_base?: boolean;
tags?: string[];
}
Stream Types
// types/stream.ts
export interface Stream {
id: string;
user_address: string;
stream_id: string;
stream_key: string;
playback_id: string;
title?: string;
description?: string;
status: StreamStatus;
viewer_count: number;
started_at?: string;
ended_at?: string;
created_at: string;
playback_url: string;
rtmp_url: string;
}
export type StreamStatus = 'idle' | 'live' | 'ended';
export interface StreamAsset {
id: string;
playback_id: string;
playback_url: string;
download_url?: string;
status: {
phase: 'waiting' | 'processing' | 'ready' | 'failed';
progress?: number;
};
duration_seconds?: number;
size_bytes?: number;
created_at: string;
}
export interface CreateStreamRequest {
userAddress: string;
title?: string;
description?: string;
}
Channel Types
// types/channel.ts
export interface Channel {
id: string;
name: string;
description?: string;
emoji: string;
category: string;
creator_address?: string;
is_official: boolean;
member_count: number;
message_count: number;
is_active: boolean;
created_at: string;
}
export interface ChannelMessage {
id: string;
channel_id: string;
sender_address: string;
content: string;
message_type: 'text' | 'image' | 'system';
is_pinned: boolean;
pinned_by?: string;
pinned_at?: string;
created_at: string;
}
export interface ChannelMember {
id: string;
channel_id: string;
user_address: string;
joined_at: string;
notifications_muted: boolean;
}
Room & Call Types
// types/room.ts
export interface Room {
id: string;
code: string;
creator_address: string;
title?: string;
type: 'instant' | 'permanent';
huddle_room_id: string;
created_at: string;
expires_at?: string;
}
export interface CallHistory {
id: string;
caller_address: string;
callee_address: string;
call_type: 'audio' | 'video';
status: 'completed' | 'missed' | 'declined' | 'failed';
started_at: string;
ended_at?: string;
duration_seconds: number;
}
API Client
Base Client
// lib/spritz-client.ts
export class SpritzApiError extends Error {
constructor(
public code: string,
message: string,
public retryAfter?: number
) {
super(message);
this.name = 'SpritzApiError';
}
}
export class SpritzClient {
private baseUrl: string;
constructor(baseUrl = 'https://app.spritz.chat/api') {
this.baseUrl = baseUrl;
}
private async request<T>(
endpoint: string,
options: RequestInit = {}
): Promise<T> {
const response = await fetch(`${this.baseUrl}${endpoint}`, {
...options,
credentials: 'include',
headers: {
'Content-Type': 'application/json',
...options.headers,
},
});
const data = await response.json();
if (!response.ok || data.success === false) {
const error = data.error || { code: 'UNKNOWN', message: 'Unknown error' };
throw new SpritzApiError(
error.code,
error.message,
error.retryAfter
);
}
return data;
}
// ==================
// Agent Methods
// ==================
async listAgents(params?: {
userAddress?: string;
visibility?: AgentVisibility;
limit?: number;
}): Promise<{ agents: Agent[] }> {
const searchParams = new URLSearchParams();
if (params?.userAddress) searchParams.set('userAddress', params.userAddress);
if (params?.visibility) searchParams.set('visibility', params.visibility);
if (params?.limit) searchParams.set('limit', params.limit.toString());
return this.request(`/agents?${searchParams}`);
}
async getAgent(id: string): Promise<{ agent: Agent }> {
return this.request(`/agents/${id}`);
}
async createAgent(data: CreateAgentRequest): Promise<{ agent: Agent }> {
return this.request('/agents', {
method: 'POST',
body: JSON.stringify(data),
});
}
async deleteAgent(id: string): Promise<{ success: boolean }> {
return this.request(`/agents/${id}`, { method: 'DELETE' });
}
async chatWithAgent(
agentId: string,
message: string,
sessionId?: string
): Promise<ChatResponse> {
return this.request(`/agents/${agentId}/chat`, {
method: 'POST',
body: JSON.stringify({ message, sessionId }),
});
}
async getChatHistory(
agentId: string,
sessionId: string,
limit = 20
): Promise<{ messages: ChatMessage[] }> {
return this.request(
`/agents/${agentId}/chat?sessionId=${sessionId}&limit=${limit}`
);
}
async discoverAgents(params?: {
tags?: string[];
limit?: number;
offset?: number;
}): Promise<{ agents: Agent[]; total: number }> {
const searchParams = new URLSearchParams();
if (params?.tags) searchParams.set('tags', params.tags.join(','));
if (params?.limit) searchParams.set('limit', params.limit.toString());
if (params?.offset) searchParams.set('offset', params.offset.toString());
return this.request(`/agents/discover?${searchParams}`);
}
// ==================
// Stream Methods
// ==================
async listStreams(params?: {
userAddress?: string;
live?: boolean;
limit?: number;
}): Promise<{ streams: Stream[] }> {
const searchParams = new URLSearchParams();
if (params?.userAddress) searchParams.set('userAddress', params.userAddress);
if (params?.live !== undefined) searchParams.set('live', params.live.toString());
if (params?.limit) searchParams.set('limit', params.limit.toString());
return this.request(`/streams?${searchParams}`);
}
async createStream(data: CreateStreamRequest): Promise<{ stream: Stream }> {
return this.request('/streams', {
method: 'POST',
body: JSON.stringify(data),
});
}
async getStream(id: string): Promise<{ stream: Stream }> {
return this.request(`/streams/${id}`);
}
async deleteStream(id: string): Promise<{ success: boolean }> {
return this.request(`/streams/${id}`, { method: 'DELETE' });
}
async getStreamAssets(streamId: string): Promise<{ assets: StreamAsset[] }> {
return this.request(`/streams/${streamId}/assets`);
}
// ==================
// Channel Methods
// ==================
async listChannels(params?: {
category?: string;
userAddress?: string;
joined?: boolean;
}): Promise<{ channels: Channel[] }> {
const searchParams = new URLSearchParams();
if (params?.category) searchParams.set('category', params.category);
if (params?.userAddress) searchParams.set('userAddress', params.userAddress);
if (params?.joined !== undefined) searchParams.set('joined', params.joined.toString());
return this.request(`/channels?${searchParams}`);
}
async joinChannel(channelId: string, userAddress: string): Promise<{ success: boolean }> {
return this.request(`/channels/${channelId}/join`, {
method: 'POST',
body: JSON.stringify({ userAddress }),
});
}
async leaveChannel(channelId: string, userAddress: string): Promise<{ success: boolean }> {
return this.request(`/channels/${channelId}/leave`, {
method: 'POST',
body: JSON.stringify({ userAddress }),
});
}
async getChannelMessages(
channelId: string,
limit = 50
): Promise<{ messages: ChannelMessage[] }> {
return this.request(`/channels/${channelId}/messages?limit=${limit}`);
}
async sendChannelMessage(
channelId: string,
userAddress: string,
content: string
): Promise<{ message: ChannelMessage }> {
return this.request(`/channels/${channelId}/messages`, {
method: 'POST',
body: JSON.stringify({ userAddress, content, type: 'text' }),
});
}
// ==================
// Room Methods
// ==================
async createRoom(data: {
type: 'instant' | 'permanent';
title?: string;
}): Promise<{ room: Room }> {
return this.request('/rooms', {
method: 'POST',
body: JSON.stringify(data),
});
}
async getRoom(code: string): Promise<{ room: Room }> {
return this.request(`/rooms/${code}`);
}
async getRoomToken(
code: string,
userAddress: string
): Promise<{ token: string }> {
return this.request(`/rooms/${code}/token`, {
method: 'POST',
body: JSON.stringify({ userAddress }),
});
}
// ==================
// User Methods
// ==================
async getPublicUser(address: string): Promise<{ user: UserProfile }> {
return this.request(`/public/user?address=${address}`);
}
async getUsername(address: string): Promise<{ username: string | null }> {
return this.request(`/username?address=${address}`);
}
async claimUsername(
address: string,
username: string
): Promise<{ success: boolean }> {
return this.request('/username', {
method: 'POST',
body: JSON.stringify({ address, username }),
});
}
}
Usage Examples
Basic Usage
import { SpritzClient, SpritzApiError } from './lib/spritz-client';
import type { Agent, ChatResponse } from './types/agent';
const client = new SpritzClient();
// List public agents
async function getPublicAgents(): Promise<Agent[]> {
try {
const { agents } = await client.listAgents({ visibility: 'public' });
return agents;
} catch (error) {
if (error instanceof SpritzApiError) {
console.error(`API Error: ${error.code} - ${error.message}`);
if (error.retryAfter) {
console.log(`Retry after ${error.retryAfter} seconds`);
}
}
throw error;
}
}
// Chat with an agent
async function chat(agentId: string, message: string): Promise<ChatResponse> {
return client.chatWithAgent(agentId, message);
}
React Hook Example
import { useState, useCallback } from 'react';
import { SpritzClient, SpritzApiError } from './lib/spritz-client';
import type { Agent, ChatMessage, ChatResponse } from './types/agent';
const client = new SpritzClient();
export function useAgentChat(agentId: string) {
const [messages, setMessages] = useState<ChatMessage[]>([]);
const [sessionId, setSessionId] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const sendMessage = useCallback(async (content: string) => {
setIsLoading(true);
setError(null);
// Add user message immediately
const userMessage: ChatMessage = {
id: crypto.randomUUID(),
role: 'user',
content,
created_at: new Date().toISOString(),
};
setMessages(prev => [...prev, userMessage]);
try {
const response = await client.chatWithAgent(
agentId,
content,
sessionId ?? undefined
);
// Store session ID for conversation continuity
if (!sessionId) {
setSessionId(response.sessionId);
}
// Add assistant message
const assistantMessage: ChatMessage = {
id: crypto.randomUUID(),
role: 'assistant',
content: response.message,
created_at: new Date().toISOString(),
};
setMessages(prev => [...prev, assistantMessage]);
return response;
} catch (err) {
if (err instanceof SpritzApiError) {
setError(err.message);
} else {
setError('An unexpected error occurred');
}
throw err;
} finally {
setIsLoading(false);
}
}, [agentId, sessionId]);
const clearChat = useCallback(() => {
setMessages([]);
setSessionId(null);
setError(null);
}, []);
return {
messages,
sendMessage,
clearChat,
isLoading,
error,
sessionId,
};
}
Error Handling
import { SpritzApiError } from './lib/spritz-client';
async function handleApiCall<T>(apiCall: () => Promise<T>): Promise<T | null> {
try {
return await apiCall();
} catch (error) {
if (error instanceof SpritzApiError) {
switch (error.code) {
case 'UNAUTHORIZED':
case 'SESSION_EXPIRED':
// Redirect to login
window.location.href = '/login';
break;
case 'RATE_LIMIT_EXCEEDED':
// Wait and retry
if (error.retryAfter) {
await new Promise(r =>
setTimeout(r, error.retryAfter! * 1000)
);
return handleApiCall(apiCall);
}
break;
case 'NOT_FOUND':
console.error('Resource not found');
break;
default:
console.error(`API Error: ${error.message}`);
}
}
return null;
}
}
x402 Payment Integration
For agents with x402 enabled, use the x402-fetch wrapper:
import { wrapFetchWithPayment } from 'x402-fetch';
import { createWalletClient, http } from 'viem';
import { base } from 'viem/chains';
import { privateKeyToAccount } from 'viem/accounts';
// Setup wallet client
const account = privateKeyToAccount('0x...');
const walletClient = createWalletClient({
account,
chain: base,
transport: http(),
});
// Wrap fetch with x402 payment handling
const fetchWithPay = wrapFetchWithPayment(fetch, walletClient);
// Make paid request to agent
async function chatWithPaidAgent(agentId: string, message: string) {
const response = await fetchWithPay(
`https://app.spritz.chat/api/public/agents/${agentId}/chat`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message }),
}
);
return response.json();
}
Type Generation
To generate types from your Spritz database schema:
# If using Supabase CLI
npx supabase gen types typescript --project-id your-project-id > types/database.ts
Next Steps
- API Reference - Complete endpoint documentation
- Error Codes - Error handling reference
- Authentication - Auth implementation details