Practical examples
Written By Stanislas
Last updated 16 days ago
Complete, ready-to-use examples for integrating the Swiftask Public Bot API into your application. Learn through working implementations that combine authentication, sessions, messaging, and subscriptions.
Overview
This guide provides five complete, production-ready examples that demonstrate different integration patterns. Each example builds upon the previous guides and shows how to combine multiple API features into cohesive applications. You can copy these examples and adapt them to your specific use case.
The examples progress from a simple complete client implementation to React hooks, Vue composition API, simpler quick-start patterns, and robust error handling.
Prerequisites
Before using these examples, ensure you have:
Read the foundational guides β Understand Authentication, Sessions, Chat Operations, and Subscriptions
Apollo Client installed β
npm install @apollo/client graphqlYour client token β From your Swiftask agent's Developer section
Basic JavaScript knowledge β These examples use async/await and modern JavaScript
Example 1: Complete chat client
A full-featured chat client that handles authentication, session management, message sending, and real-time streaming in one class.
import { ApolloClient, InMemoryCache, createHttpLink, gql, split } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
import { WebSocketLink } from '@apollo/client/link/ws';
import { getMainDefinition } from '@apollo/client/utilities';
class SwiftaskChatClient {
constructor(clientToken) {
this.clientToken = clientToken;
this.clientUuid = this.getOrCreateClientUuid();
this.apiUrl = 'https://graphql.swiftask.ai';
this.config = null;
this.client = null;
}
getOrCreateClientUuid() {
let uuid = localStorage.getItem('swiftask_client_uuid');
if (!uuid) {
uuid = crypto.randomUUID();
localStorage.setItem('swiftask_client_uuid', uuid);
}
return uuid;
}
async authenticate() {
console.log('Authenticating...');
const response = await fetch(`${this.apiUrl}/public/widget-bot/${this.clientToken}`, {
method: 'GET',
headers: {
'x-client-uuid': this.clientUuid,
'Content-Type': 'application/json',
},
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error?.message || 'Authentication failed');
}
const { data } = await response.json();
this.config = data;
console.log('β Authenticated successfully');
this.setupApolloClient();
return this.config;
}
setupApolloClient() {
const httpLink = createHttpLink({
uri: `${this.apiUrl}/graphql`,
});
const authLink = setContext((_, { headers }) => ({
headers: {
...headers,
authorization: `Bearer ${this.config.accessToken}`,
'x-workspace-id': this.config.workspaceId,
'x-client': 'widget',
},
}));
const wsLink = new WebSocketLink({
uri: 'wss://graphql.swiftask.ai/graphql',
options: {
reconnect: true,
connectionParams: {
authorization: `Bearer ${this.config.accessToken}`,
workspaceId: this.config.workspaceId,
},
},
});
const splitLink = split(
({ query }) => {
const definition = getMainDefinition(query);
return definition.kind === 'OperationDefinition' && definition.operation === 'subscription';
},
wsLink,
authLink.concat(httpLink)
);
this.client = new ApolloClient({
link: splitLink,
cache: new InMemoryCache(),
});
}
async createSession(title = 'New Chat') {
console.log('Creating session:', title);
const CREATE_SESSION = gql`
mutation CreateTodoChatSession($data: TodoChatSessionInput!) {
createTodoChatSession(data: $data) {
id
title
createdAt
defaultBotId
}
}
`;
const { data } = await this.client.mutate({
mutation: CREATE_SESSION,
variables: {
data: {
todoId: this.config.todoId,
title,
},
},
});
console.log('β Session created:', data.createTodoChatSession.id);
return data.createTodoChatSession;
}
async sendMessage(sessionId, message) {
console.log('Sending message:', message);
const SEND_MESSAGE = gql`
mutation SendNewMessage($newMessageData: NewMessageInput!) {
sendNewMessage(newMessageData: $newMessageData) {
id
message
createdAt
sessionId
}
}
`;
const { data } = await this.client.mutate({
mutation: SEND_MESSAGE,
variables: {
newMessageData: {
message,
sessionId,
isForAiReply: true,
},
},
});
console.log('β Message sent:', data.sendNewMessage.id);
return data.sendNewMessage;
}
subscribeToMessages(sessionId, onMessage) {
console.log('Subscribing to messages for session:', sessionId);
const NEW_MESSAGE_SUBSCRIPTION = gql`
subscription NewMessage($sessionId: Float!) {
newMessage(sessionId: $sessionId) {
id
message
createdAt
sentBy {
firstName
}
isBotReply
}
}
`;
return this.client
.subscribe({
query: NEW_MESSAGE_SUBSCRIPTION,
variables: { sessionId },
})
.subscribe({
next: ({ data }) => {
console.log('New message:', data.newMessage.message);
onMessage(data.newMessage);
},
error: (error) => {
console.error('Subscription error:', error);
},
complete: () => {
console.log('Subscription completed');
},
});
}
subscribeToStream(sessionId, onChunk, onComplete) {
console.log('Subscribing to message stream for session:', sessionId);
const STREAM_SUBSCRIPTION = gql`
subscription OnMessageStream($sessionId: Float!) {
onMessageStream(sessionId: $sessionId) {
messageChunk
botResponseMessageId
isStoppable
}
}
`;
let fullMessage = '';
return this.client
.subscribe({
query: STREAM_SUBSCRIPTION,
variables: { sessionId },
})
.subscribe({
next: ({ data }) => {
fullMessage += data.onMessageStream.messageChunk;
onChunk(fullMessage, data.onMessageStream);
},
error: (error) => {
console.error('Stream error:', error);
},
complete: () => {
console.log('Stream completed');
if (onComplete) onComplete(fullMessage);
},
});
}
subscribeToToolCalls(sessionId, onToolCall) {
const TOOL_SUBSCRIPTION = gql`
subscription OnAgentToolCall($sessionId: Float!) {
onAgentToolCall(sessionId: $sessionId) {
tool
status
toolImage
}
}
`;
return this.client
.subscribe({
query: TOOL_SUBSCRIPTION,
variables: { sessionId },
})
.subscribe({
next: ({ data }) => {
onToolCall(data.onAgentToolCall);
},
});
}
async stopGeneration(sessionId, botId) {
const STOP_MUTATION = gql`
mutation StopAIResponse($sessionId: Float!, $botId: Float!) {
stopAIResponse(sessionId: $sessionId, botId: $botId)
}
`;
await this.client.mutate({
mutation: STOP_MUTATION,
variables: { sessionId, botId },
});
}
getClient() {
return this.client;
}
getConfig() {
return this.config;
}
}
// Usage example
async function main() {
const chat = new SwiftaskChatClient('your_client_token_here');
try {
// Step 1: Authenticate
const config = await chat.authenticate();
console.log('Bot slug:', config.botSlug);
// Step 2: Create a new session
const session = await chat.createSession('My First Chat');
// Step 3: Subscribe to new messages
const messageSub = chat.subscribeToMessages(session.id, (message) => {
const sender = message.isBotReply ? 'π€ Bot' : 'π€ You';
console.log(`${sender}: ${message.message}`);
});
// Step 4: Subscribe to streaming
const streamSub = chat.subscribeToStream(
session.id,
(fullMessage) => {
console.log('Streaming:', fullMessage);
},
(finalMessage) => {
console.log('β Response complete');
}
);
// Step 5: Subscribe to tool calls
const toolSub = chat.subscribeToToolCalls(session.id, (toolCall) => {
if (toolCall.status === 'START') {
console.log(`π§ Using ${toolCall.tool}...`);
} else if (toolCall.status === 'END') {
console.log(`β ${toolCall.tool} completed`);
}
});
// Step 6: Send a message
await chat.sendMessage(session.id, 'Hello! What can you help me with?');
// The response will come through the subscriptions
// Cleanup when done
setTimeout(() => {
messageSub.unsubscribe();
streamSub.unsubscribe();
toolSub.unsubscribe();
}, 30000); // Cleanup after 30 seconds for demo
} catch (error) {
console.error('Error:', error);
}
}
// Run the example
main();
Example 2: React hook integration
Use a custom React hook to simplify Swiftask integration in React applications.
import { useState, useEffect, useCallback, useRef } from 'react';
import { SwiftaskChatClient } from './SwiftaskChatClient';
export function useSwiftaskChat(clientToken) {
const [client, setClient] = useState(null);
const [session, setSession] = useState(null);
const [messages, setMessages] = useState([]);
const [streamingMessage, setStreamingMessage] = useState('');
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
const subscriptionsRef = useRef([]);
// Initialize client and create session
useEffect(() => {
const initClient = async () => {
try {
const chatClient = new SwiftaskChatClient(clientToken);
await chatClient.authenticate();
const newSession = await chatClient.createSession('Chat Session');
setClient(chatClient);
setSession(newSession);
setIsLoading(false);
} catch (err) {
setError(err.message);
setIsLoading(false);
}
};
initClient();
}, [clientToken]);
// Setup subscriptions
useEffect(() => {
if (!client || !session) return;
// Subscribe to new messages
const messageSub = client.subscribeToMessages(session.id, (message) => {
setMessages((prev) => [...prev, message]);
setStreamingMessage('');
});
// Subscribe to streaming
const streamSub = client.subscribeToStream(
session.id,
(fullMessage) => {
setStreamingMessage(fullMessage);
},
() => {
setStreamingMessage('');
}
);
subscriptionsRef.current = [messageSub, streamSub];
// Cleanup
return () => {
subscriptionsRef.current.forEach((sub) => sub.unsubscribe());
};
}, [client, session]);
const sendMessage = useCallback(
async (text) => {
if (!client || !session) return;
try {
await client.sendMessage(session.id, text);
} catch (err) {
setError(err.message);
}
},
[client, session]
);
return {
messages,
streamingMessage,
sendMessage,
isLoading,
error,
session,
};
}
// React component using the hook
function ChatComponent() {
const { messages, streamingMessage, sendMessage, isLoading, error } = useSwiftaskChat(
'your_client_token'
);
const [input, setInput] = useState('');
const handleSend = () => {
if (input.trim()) {
sendMessage(input);
setInput('');
}
};
if (isLoading) return <div className="loading">Loading chat...</div>;
if (error) return <div className="error">Error: {error}</div>;
return (
<div className="chat-container">
<div className="messages">
{messages.map((msg) => (
<div key={msg.id} className={`message ${msg.isBotReply ? 'bot' : 'user'}`}>
<strong>{msg.isBotReply ? 'Bot' : 'You'}:</strong> {msg.message}
</div>
))}
{streamingMessage && <div className="message bot streaming">Bot: {streamingMessage}</div>}
</div>
<div className="input-area">
<input
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && handleSend()}
placeholder="Type a message..."
disabled={isLoading}
/>
<button onClick={handleSend} disabled={isLoading}>
Send
</button>
</div>
</div>
);
}
export default ChatComponent;
Example 3: Vue composition API integration
Integrate Swiftask into Vue 3 applications using the Composition API.
import { ref, onMounted, onUnmounted } from 'vue';
import { SwiftaskChatClient } from './SwiftaskChatClient';
export function useSwiftaskChat(clientToken) {
const client = ref(null);
const session = ref(null);
const messages = ref([]);
const streamingMessage = ref('');
const isLoading = ref(true);
const error = ref(null);
const subscriptions = [];
const initialize = async () => {
try {
const chatClient = new SwiftaskChatClient(clientToken);
await chatClient.authenticate();
const newSession = await chatClient.createSession('Chat Session');
client.value = chatClient;
session.value = newSession;
setupSubscriptions();
isLoading.value = false;
} catch (err) {
error.value = err.message;
isLoading.value = false;
}
};
const setupSubscriptions = () => {
const messageSub = client.value.subscribeToMessages(session.value.id, (message) => {
messages.value.push(message);
streamingMessage.value = '';
});
const streamSub = client.value.subscribeToStream(
session.value.id,
(fullMessage) => {
streamingMessage.value = fullMessage;
},
() => {
streamingMessage.value = '';
}
);
subscriptions.push(messageSub, streamSub);
};
const sendMessage = async (text) => {
if (!client.value || !session.value) return;
try {
await client.value.sendMessage(session.value.id, text);
} catch (err) {
error.value = err.message;
}
};
onMounted(initialize);
onUnmounted(() => {
subscriptions.forEach((sub) => sub.unsubscribe());
});
return {
messages,
streamingMessage,
sendMessage,
isLoading,
error,
};
}
// Vue component using the composition function
import { defineComponent } from 'vue';
export default defineComponent({
name: 'ChatComponent',
setup() {
const { messages, streamingMessage, sendMessage, isLoading, error } = useSwiftaskChat(
'your_client_token'
);
const input = ref('');
const handleSend = () => {
if (input.value.trim()) {
sendMessage(input.value);
input.value = '';
}
};
return {
messages,
streamingMessage,
input,
handleSend,
isLoading,
error,
};
},
template: `
<div class="chat-container" v-if="!isLoading">
<div v-if="error" class="error">Error: {{ error }}</div>
<div class="messages">
<div v-for="msg in messages" :key="msg.id" :class="['message', msg.isBotReply ? 'bot' : 'user']">
<strong>{{ msg.isBotReply ? 'Bot' : 'You' }}:</strong> {{ msg.message }}
</div>
<div v-if="streamingMessage" class="message bot streaming">
Bot: {{ streamingMessage }}
</div>
</div>
<div class="input-area">
<input
v-model="input"
@keypress.enter="handleSend"
placeholder="Type a message..."
:disabled="isLoading"
/>
<button @click="handleSend" :disabled="isLoading">Send</button>
</div>
</div>
<div v-else class="loading">Loading chat...</div>
`,
});
Example 4: Quick start with starter session
For simple use cases, use the automatically created starter session without creating a new one.
class SimpleSwiftaskChat {
constructor(clientToken) {
this.clientToken = clientToken;
this.clientUuid = crypto.randomUUID();
this.client = null;
this.sessionId = null;
}
async initialize() {
// Step 1: Authenticate
const response = await fetch(
`https://graphql.swiftask.ai/public/widget-bot/${this.clientToken}`,
{
headers: { 'x-client-uuid': this.clientUuid },
}
);
const { data } = await response.json();
// Step 2: Setup GraphQL client
const httpLink = createHttpLink({
uri: 'https://graphql.swiftask.ai/graphql',
});
const authLink = setContext((_, { headers }) => ({
headers: {
...headers,
authorization: `Bearer ${data.accessToken}`,
'x-workspace-id': data.workspaceId,
'x-client': 'widget',
},
}));
this.client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
});
// Step 3: Use the starter session (no need to create)
this.sessionId = data.starterSessionId;
console.log('Ready to chat in session:', this.sessionId);
}
async sendMessage(messageText) {
const SEND_MESSAGE = gql`
mutation SendNewMessage($newMessageData: NewMessageInput!) {
sendNewMessage(newMessageData: $newMessageData) {
id
message
}
}
`;
const { data } = await this.client.mutate({
mutation: SEND_MESSAGE,
variables: {
newMessageData: {
message: messageText,
sessionId: this.sessionId,
isForAiReply: true,
},
},
});
return data.sendNewMessage;
}
subscribeToStream(onChunk) {
const STREAM = gql`
subscription OnMessageStream($sessionId: Float!) {
onMessageStream(sessionId: $sessionId) {
messageChunk
}
}
`;
let fullMessage = '';
return this.client
.subscribe({
query: STREAM,
variables: { sessionId: this.sessionId },
})
.subscribe({
next: ({ data }) => {
fullMessage += data.onMessageStream.messageChunk;
onChunk(fullMessage);
},
});
}
}
// Usage - much simpler!
async function main() {
const chat = new SimpleSwiftaskChat('your_client_token');
await chat.initialize();
// Start chatting immediately
await chat.sendMessage('Hello!');
chat.subscribeToStream((fullMessage) => {
console.log('Response:', fullMessage);
});
}
Example 5: Robust error handling and retry logic
Production-ready implementation with comprehensive error handling and automatic retries.
class RobustSwiftaskChat {
constructor(clientToken, options = {}) {
this.clientToken = clientToken;
this.clientUuid = crypto.randomUUID();
this.client = null;
this.sessionId = null;
this.maxRetries = options.maxRetries || 3;
this.retryDelay = options.retryDelay || 1000;
this.onError = options.onError || console.error;
this.onSuccess = options.onSuccess || console.log;
}
async retryOperation(operation, retries = this.maxRetries) {
try {
return await operation();
} catch (error) {
if (retries > 0 && this.isRetryableError(error)) {
console.log(`Retrying... (${this.maxRetries - retries + 1}/${this.maxRetries})`);
await this.sleep(this.retryDelay);
return this.retryOperation(operation, retries - 1);
}
throw error;
}
}
isRetryableError(error) {
// Don't retry auth errors
if (error.message?.includes('Unauthorized') || error.message?.includes('Authentication failed')) {
return false;
}
// Retry network errors
if (error instanceof TypeError || error.message?.includes('network')) {
return true;
}
// Retry server errors (5xx)
if (error.status >= 500) {
return true;
}
return true;
}
sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async initialize() {
try {
await this.retryOperation(async () => {
const response = await fetch(
`https://graphql.swiftask.ai/public/widget-bot/${this.clientToken}`,
{
headers: { 'x-client-uuid': this.clientUuid },
}
);
if (!response.ok) {
throw new Error(`Authentication failed: ${response.status}`);
}
const { data } = await response.json();
// Setup client
const httpLink = createHttpLink({
uri: 'https://graphql.swiftask.ai/graphql',
});
const authLink = setContext((_, { headers }) => ({
headers: {
...headers,
authorization: `Bearer ${data.accessToken}`,
'x-workspace-id': data.workspaceId,
'x-client': 'widget',
},
}));
this.client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
});
this.sessionId = data.starterSessionId;
this.onSuccess('Chat initialized successfully');
});
} catch (error) {
this.onError('Failed to initialize chat:', error);
throw error;
}
}
async sendMessage(messageText) {
try {
return await this.retryOperation(async () => {
const SEND_MESSAGE = gql`
mutation SendNewMessage($newMessageData: NewMessageInput!) {
sendNewMessage(newMessageData: $newMessageData) {
id
message
}
}
`;
const { data } = await this.client.mutate({
mutation: SEND_MESSAGE,
variables: {
newMessageData: {
message: messageText,
sessionId: this.sessionId,
isForAiReply: true,
},
},
});
return data.sendNewMessage;
});
} catch (error) {
this.onError('Failed to send message:', error);
throw error;
}
}
subscribeToStreamWithErrorHandling(onChunk, onError) {
const STREAM = gql`
subscription OnMessageStream($sessionId: Float!) {
onMessageStream(sessionId: $sessionId) {
messageChunk
}
}
`;
let fullMessage = '';
let retryCount = 0;
const subscribe = () => {
return this.client
.subscribe({
query: STREAM,
variables: { sessionId: this.sessionId },
})
.subscribe({
next: ({ data }) => {
fullMessage += data.onMessageStream.messageChunk;
onChunk(fullMessage);
retryCount = 0; // Reset retry count on success
},
error: (error) => {
console.error('Stream error:', error);
if (error.message?.includes('authorization')) {
onError('Authentication expired. Please refresh.');
} else if (error.message?.includes('network')) {
retryCount++;
if (retryCount < 3) {
console.log(`Reconnecting... (${retryCount}/3)`);
setTimeout(() => {
subscribe();
}, 5000);
} else {
onError('Connection lost. Please try again.');
}
} else {
onError('Stream error: ' + error.message);
}
},
complete: () => {
this.onSuccess('Stream completed');
},
});
};
return subscribe();
}
}
// Usage with error handling
async function main() {
const chat = new RobustSwiftaskChat('your_client_token', {
maxRetries: 3,
retryDelay: 1000,
onError: (message, error) => {
console.error(message, error);
showErrorToUser(message);
},
onSuccess: (message) => {
console.log(message);
},
});
try {
await chat.initialize();
await chat.sendMessage('Hello!');
chat.subscribeToStreamWithErrorHandling(
(fullMessage) => {
updateUI(fullMessage);
},
(errorMessage) => {
showErrorToUser(errorMessage);
}
);
} catch (error) {
console.error('Fatal error:', error);
}
}
function showErrorToUser(message) {
// Show error in UI
const errorElement = document.createElement('div');
errorElement.className = 'error-message';
errorElement.textContent = message;
document.body.appendChild(errorElement);
}
function updateUI(message) {
const messageElement = document.getElementById('response');
messageElement.textContent = message;
}
Common patterns
Handling multiple messages
// Send multiple messages in sequence
async function sendMultipleMessages(chat, sessionId, messages) {
for (const msg of messages) {
await chat.sendMessage(sessionId, msg);
await new Promise((resolve) => setTimeout(resolve, 500)); // Delay between messages
}
}
// Usage
await sendMultipleMessages(chat, sessionId, [
'What is your pricing?',
'Do you offer discounts?',
'Can I try for free?',
]);
Handling session persistence
// Save session ID to localStorage
function saveSession(sessionId) {
localStorage.setItem('swiftask_session_id', sessionId);
}
// Retrieve session on page reload
function getPersistedSession() {
return localStorage.getItem('swiftask_session_id');
}
// Usage
const persistedSessionId = getPersistedSession();
if (persistedSessionId) {
// Continue with existing session
await chat.sendMessage(persistedSessionId, 'Hello again!');
} else {
// Create new session
const session = await chat.createSession();
saveSession(session.id);
}
Handling user input validation
function validateMessage(message) {
if (!message || message.trim().length === 0) {
throw new Error('Message cannot be empty');
}
if (message.length > 5000) {
throw new Error('Message is too long (max 5000 characters)');
}
return message.trim();
}
// Usage
const handleSend = async (input) => {
try {
const validatedMessage = validateMessage(input);
await chat.sendMessage(sessionId, validatedMessage);
} catch (error) {
showErrorToUser(error.message);
}
};