Skip to Content
SDKs & ExamplesJavaScript / TypeScript

JavaScript / TypeScript

Complete code examples for integrating with the VelaFlows API using JavaScript or TypeScript. All examples use the native fetch API (Node.js 18+ or browser).

Setup: API Client Helper

Create a reusable API client to handle authentication, error handling, and pagination:

// velaflows.ts const BASE_URL = 'https://api.velaflows.com/api/v1' interface VelaFlowsConfig { token: string workspaceId: string } interface ApiResponse<T> { success: boolean data: T error?: { code: string message: string details?: Record<string, unknown> } } interface PaginatedData<T> { items: T[] pagination: { page: number limit: number total: number totalPages: number } } class VelaFlowsClient { private token: string private workspaceId: string constructor(config: VelaFlowsConfig) { this.token = config.token this.workspaceId = config.workspaceId } private get headers(): Record<string, string> { return { 'Authorization': `Bearer ${this.token}`, 'x-workspace-id': this.workspaceId, 'Content-Type': 'application/json', } } async get<T>(path: string, params?: Record<string, string>): Promise<T> { const url = new URL(`${BASE_URL}${path}`) if (params) { Object.entries(params).forEach(([key, value]) => { if (value !== undefined && value !== '') { url.searchParams.set(key, value) } }) } const response = await fetch(url.toString(), { headers: this.headers }) return this.handleResponse<T>(response) } async post<T>(path: string, body?: unknown): Promise<T> { const response = await fetch(`${BASE_URL}${path}`, { method: 'POST', headers: this.headers, body: body ? JSON.stringify(body) : undefined, }) return this.handleResponse<T>(response) } async patch<T>(path: string, body: unknown): Promise<T> { const response = await fetch(`${BASE_URL}${path}`, { method: 'PATCH', headers: this.headers, body: JSON.stringify(body), }) return this.handleResponse<T>(response) } async delete<T>(path: string): Promise<T> { const response = await fetch(`${BASE_URL}${path}`, { method: 'DELETE', headers: this.headers, }) return this.handleResponse<T>(response) } private async handleResponse<T>(response: Response): Promise<T> { const json = (await response.json()) as ApiResponse<T> if (!response.ok || !json.success) { const error = json.error || { code: 'UNKNOWN', message: 'Unknown error' } throw new VelaFlowsError(response.status, error.code, error.message, error.details) } return json.data } } class VelaFlowsError extends Error { constructor( public status: number, public code: string, message: string, public details?: Record<string, unknown> ) { super(message) this.name = 'VelaFlowsError' } } // Initialize the client const velaflows = new VelaFlowsClient({ token: process.env.VELAFLOWS_API_TOKEN!, workspaceId: process.env.VELAFLOWS_WORKSPACE_ID!, }) export { velaflows, VelaFlowsClient, VelaFlowsError }

Common Operations

List Conversations

interface Conversation { _id: string channelType: string status: string priority: string assignedAgentId: string | null lastMessageAt: string unreadCount: number createdAt: string } // List open conversations, page 1 const result = await velaflows.get<PaginatedData<Conversation>>( '/inbox/conversations', { page: '1', limit: '20', status: 'open' } ) console.log(`Total conversations: ${result.pagination.total}`) result.items.forEach(conv => { console.log(`[${conv.channelType}] ${conv.status} - ${conv.lastMessageAt}`) })

Create a CRM Lead

interface Lead { _id: string firstName: string lastName: string email: string phone: string pipelineId: string stageId: string createdAt: string } const lead = await velaflows.post<{ lead: Lead }>('/crm/leads', { firstName: 'Jane', lastName: 'Smith', email: 'jane.smith@example.com', phone: '+1234567890', pipelineId: '65a1b2c3d4e5f6a7b8c9d0e1', stageId: '65a1b2c3d4e5f6a7b8c9d0e2', }) console.log(`Created lead: ${lead.lead._id}`)

Send a Message

const message = await velaflows.post<{ message: { _id: string } }>( '/inbox/conversations/65a1b2c3d4e5f6a7b8c9d0e1/messages', { content: 'Hello! How can I help you today?', type: 'text', } ) console.log(`Sent message: ${message.message._id}`)

Update a Customer

const customer = await velaflows.patch<{ lead: Lead }>( '/customers/65a1b2c3d4e5f6a7b8c9d0e1', { firstName: 'Jane', lastName: 'Doe', email: 'jane.doe@newcompany.com', } ) console.log(`Updated customer: ${customer.lead._id}`)

Subscribe to Webhooks

const subscription = await velaflows.post<{ subscription: { _id: string } }>( '/webhooks/subscriptions', { url: 'https://your-app.com/webhooks/velaflows', events: [ 'conversation.created', 'conversation.message.received', 'lead.stage_changed', ], secret: 'your-webhook-secret-for-verification', } ) console.log(`Webhook subscription: ${subscription.subscription._id}`)

Error Handling

try { const lead = await velaflows.post<{ lead: Lead }>('/crm/leads', { email: 'invalid-email', }) } catch (error) { if (error instanceof VelaFlowsError) { switch (error.code) { case 'VALIDATION_ERROR': console.error('Invalid data:', error.details) break case 'UNAUTHORIZED': console.error('Check your API token') break case 'FORBIDDEN': console.error('Token lacks required scope') break case 'TOO_MANY_REQUESTS': console.error('Rate limited — retry later') break default: console.error(`API error [${error.code}]: ${error.message}`) } } else { console.error('Network error:', error) } }

Pagination Helper

Fetch all pages of a paginated endpoint:

async function fetchAllPages<T>( path: string, params: Record<string, string> = {}, limit = 100 ): Promise<T[]> { const allItems: T[] = [] let page = 1 let totalPages = 1 do { const result = await velaflows.get<PaginatedData<T>>(path, { ...params, page: String(page), limit: String(limit), }) allItems.push(...result.items) totalPages = result.pagination.totalPages page++ } while (page <= totalPages) return allItems } // Usage: fetch all open conversations const allConversations = await fetchAllPages<Conversation>( '/inbox/conversations', { status: 'open' } ) console.log(`Fetched ${allConversations.length} conversations`)

Retry with Exponential Backoff

Wrap the client with automatic retry logic:

async function withRetry<T>( fn: () => Promise<T>, maxRetries = 3 ): Promise<T> { for (let attempt = 0; attempt <= maxRetries; attempt++) { try { return await fn() } catch (error) { if (error instanceof VelaFlowsError) { // Don't retry client errors (except rate limits) if (error.status < 500 && error.status !== 429) { throw error } if (attempt === maxRetries) { throw error } const delay = Math.pow(2, attempt) * 1000 + Math.random() * 1000 console.log(`Retrying in ${Math.round(delay)}ms (attempt ${attempt + 1})`) await new Promise(resolve => setTimeout(resolve, delay)) } else { throw error } } } throw new Error('Unreachable') } // Usage const conversations = await withRetry(() => velaflows.get<PaginatedData<Conversation>>('/inbox/conversations') )