Adding Lumnis AI to TrySolid
Official guide for integrating Lumnis AI API to TrySolid
Integrating Lumnis AI with TrySolid
This guide provides step-by-step, copyable prompts to integrate Lumnis AI into your TrySolid project. Each prompt is designed to be pasted directly into TrySolid for quick implementation.
What You'll Build
By following this guide, you'll create a complete AI agent application with:
- 🔌 App Integrations: Connect to external tools like Google Docs, Google Sheets, Slack, and more
- ✨ Task Creation: A simple interface where users can describe tasks in natural language
- 📊 Task Management: View and track agent tasks with real-time progress updates
- 👤 User Management: Automatic user creation and authentication
- 🤖 Intelligent Agent: An AI agent that can research, create documents, analyze data, and more
The result is a flexible agent platform that users can interact with through natural language prompts, with the agent automatically using the right tools and integrations to complete tasks.
Prerequisites
Before starting, ensure you have:
- A TrySolid account with a project created
- Your Lumnis AI API key
- Basic understanding of what you want your AI agent to do
Step 1: Build Complete AI Agent Application
We're building a complete AI agent application that allows users to:
- Connect to external tools (Google Docs, Sheets, Slack, etc.)
- Submit tasks using natural language prompts
- Track agent progress in real-time
- View past tasks and results
Install the lumnisai npm package for the project.
Create the following database schema for Lumnis AI integration:
1. profiles table:
- id (UUID, primary key)
- user_id (UUID, unique)
- email (text)
- first_name (text)
- last_name (text)
- created_at, updated_at (timestamps)
2. agent_responses table:
- id (UUID, primary key)
- user_id (UUID)
- response_id (text, unique) - stores Lumnis AI response ID
- status (text) - queued/running/succeeded/failed/cancelled
- progress (JSONB) - array of progress entries
- output_text (text) - final agent output
- error (text)
- created_at, updated_at (timestamps)
Add RLS policies so users can only access their own data. Create update_updated_at_column function and triggers for automatic timestamp updates.
Create the following backend functions. This is guidance on how to implement the backend - adapt the code to your framework:
1. CREATE ensure-user FUNCTION:
// This function ensures a Lumnis AI user exists
import { LumnisClient } from 'lumnisai';
import { z } from "zod";
const userInputSchema = z.object({
email: z.string().email().max(255),
firstName: z.string().max(100).optional(),
lastName: z.string().max(100).optional(),
});
const LUMNISAI_API_KEY = process.env.LUMNISAI_API_KEY;
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
};
async function ensureUser(req) {
if (req.method === 'OPTIONS') {
return new Response(null, { headers: corsHeaders });
}
try {
const authHeader = req.headers.get('Authorization');
if (!authHeader) {
return new Response(JSON.stringify({ error: 'Unauthorized' }), {
status: 401,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
}
// Get authenticated user from your auth system
const token = authHeader.replace('Bearer ', '');
// const user = await getAuthenticatedUser(token);
if (!user) {
return new Response(JSON.stringify({ error: 'Unauthorized' }), {
status: 401,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
}
const body = await req.json();
const validation = userInputSchema.safeParse(body);
if (!validation.success) {
return new Response(JSON.stringify({ error: 'Invalid input data' }), {
status: 400,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
}
const { email, firstName, lastName } = validation.data;
// Ensure user can only manage their own profile
if (user.email !== email) {
return new Response(JSON.stringify({ error: 'Forbidden' }), {
status: 403,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
}
if (!LUMNISAI_API_KEY) {
throw new Error('LUMNISAI_API_KEY not configured');
}
const client = new LumnisClient({ apiKey: LUMNISAI_API_KEY });
try {
const existing = await client.getUser(email);
console.log('User exists:', email);
return new Response(JSON.stringify(existing), {
status: 200,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
} catch (error) {
if (error.message?.includes('not found') || error.response?.status === 404) {
const created = await client.createUser({
email,
firstName: firstName ?? email.split('@')[0],
lastName: lastName ?? 'User',
});
console.log('User created:', email);
return new Response(JSON.stringify(created), {
status: 201,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
}
throw error;
}
} catch (error) {
console.error('Error in ensure-user:', error);
const message = error instanceof Error ? error.message : 'Unknown error';
return new Response(JSON.stringify({ error: message }), {
status: 500,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
}
}
2. CREATE initiate-connection FUNCTION:
// This function initiates app connections
import { LumnisClient } from 'lumnisai';
const LUMNISAI_API_KEY = process.env.LUMNISAI_API_KEY;
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
};
async function initiateConnection(req) {
if (req.method === 'OPTIONS') {
return new Response(null, { headers: corsHeaders });
}
try {
const authHeader = req.headers.get('Authorization');
if (!authHeader) {
throw new Error('No authorization header');
}
if (!LUMNISAI_API_KEY) {
throw new Error('LUMNISAI_API_KEY not configured');
}
// Get authenticated user from your auth system
const token = authHeader.replace('Bearer ', '');
// const user = await getAuthenticatedUser(token);
if (!user?.email) {
throw new Error('Unauthorized');
}
const { appName } = await req.json(); // e.g., 'googledocs', 'googlesheets', 'slack', etc.
console.log(`Initiating ${appName} connection for ${user.email}`);
const client = new LumnisClient({ apiKey: LUMNISAI_API_KEY });
// Enable app at tenant level if not enabled
const appEnabled = await client.isAppEnabled(appName);
if (!appEnabled.enabled) {
console.log(`Enabling ${appName} at tenant level...`);
await client.setAppEnabled(appName, true);
}
// Initiate connection
const connection = await client.initiateConnection({
userId: user.email,
appName: appName,
});
console.log(`${appName} connection initiated for ${user.email}:`, connection);
return new Response(JSON.stringify({
redirectUrl: connection.redirectUrl,
status: 'pending'
}), {
status: 200,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
} catch (error) {
console.error('Error connecting:', error);
const message = error instanceof Error ? error.message : 'Unknown error';
return new Response(JSON.stringify({ error: message }), {
status: 500,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
}
}
3. CREATE check-connection FUNCTION:
// This function checks app connection status
import { LumnisClient } from 'lumnisai';
const LUMNISAI_API_KEY = process.env.LUMNISAI_API_KEY;
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
};
async function checkConnection(req) {
if (req.method === 'OPTIONS') {
return new Response(null, { headers: corsHeaders });
}
try {
const authHeader = req.headers.get('Authorization');
if (!authHeader) {
throw new Error('No authorization header');
}
if (!LUMNISAI_API_KEY) {
throw new Error('LUMNISAI_API_KEY not configured');
}
// Get authenticated user from your auth system
const token = authHeader.replace('Bearer ', '');
// const user = await getAuthenticatedUser(token);
if (!user?.email) {
throw new Error('Unauthorized');
}
const { appName } = await req.json(); // e.g., 'googledocs', 'googlesheets', 'slack', etc.
console.log(`Checking ${appName} connection status for ${user.email}`);
const client = new LumnisClient({ apiKey: LUMNISAI_API_KEY });
const status = await client.getConnectionStatus(user.email, appName);
console.log(`${appName} status for ${user.email}:`, status);
return new Response(JSON.stringify(status), {
status: 200,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
} catch (error) {
console.error('Error checking status:', error);
const message = error instanceof Error ? error.message : 'Unknown error';
return new Response(JSON.stringify({ error: message }), {
status: 500,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
}
}
4. CREATE create-response FUNCTION:
// This function creates an AI agent response
import { LumnisClient } from "lumnisai";
// Define your agent system prompt
const AGENT_PROMPT = `You are an expert AI agent with access to various tools and integrations. Your role is to help users accomplish their tasks efficiently and accurately.
Key capabilities:
- Research and gather information from the web
- Create and edit documents in Google Docs
- Manage data in Google Sheets
- Integrate with various productivity tools
- Provide comprehensive, well-structured outputs
Guidelines:
- Break down complex tasks into manageable steps
- Use appropriate tools for each subtask
- Verify information when possible
- Provide clear explanations of your actions
- Ask for clarification if the task is ambiguous`;
const LUMNISAI_API_KEY = process.env.LUMNISAI_API_KEY;
const corsHeaders = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",
};
async function createResponse(req) {
if (req.method === "OPTIONS") {
return new Response(null, { headers: corsHeaders });
}
try {
const authHeader = req.headers.get("Authorization");
if (!authHeader) {
throw new Error("No authorization header");
}
// Get authenticated user from your auth system
const token = authHeader.replace("Bearer ", "");
// const user = await getAuthenticatedUser(token);
if (!user) {
throw new Error("Unauthorized");
}
const body = await req.json();
const { taskPrompt } = body;
if (!LUMNISAI_API_KEY) {
throw new Error("LUMNISAI_API_KEY not configured");
}
const client = new LumnisClient({ apiKey: LUMNISAI_API_KEY });
// Build the full task message
const taskMessage = `${AGENT_PROMPT}\n\nUSER TASK:\n${taskPrompt}`;
// Create LumnisAI response
const responseData = await client.responses.create({
messages: [{ role: "user", content: taskMessage }],
userId: user.email,
agentConfig: {
coordinatorModelName: "openai:gpt-4.1",
plannerModelName: "openai:gpt-4.1",
orchestratorModelName: "openai:gpt-4.1",
useCognitiveTools: true,
enableTaskValidation: true,
generateComprehensiveOutput: false,
},
});
const responseId = responseData.responseId;
// Store in your database
// await database.insert('agent_responses', {
// user_id: user.id,
// response_id: responseId,
// status: "queued",
// });
console.log("Response created:", responseId);
console.log("User:", user.email);
return new Response(JSON.stringify({ responseId }), {
status: 201,
headers: { ...corsHeaders, "Content-Type": "application/json" },
});
} catch (error) {
console.error("Error in create-response:", error);
const message = error instanceof Error ? error.message : "Unknown error";
return new Response(JSON.stringify({ error: message }), {
status: 500,
headers: { ...corsHeaders, "Content-Type": "application/json" },
});
}
}
5. CREATE poll-response FUNCTION:
// This function polls for agent response updates
import { LumnisClient } from 'lumnisai';
const LUMNISAI_API_KEY = process.env.LUMNISAI_API_KEY;
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
};
async function pollResponse(req) {
if (req.method === 'OPTIONS') {
return new Response(null, { headers: corsHeaders });
}
try {
const { responseId } = await req.json();
if (!responseId) {
throw new Error('Missing responseId');
}
const authHeader = req.headers.get('Authorization');
if (!authHeader) {
throw new Error('No authorization header');
}
if (!LUMNISAI_API_KEY) {
throw new Error('LUMNISAI_API_KEY not configured');
}
// Get authenticated user from your auth system
const token = authHeader.replace('Bearer ', '');
// const user = await getAuthenticatedUser(token);
if (!user) {
throw new Error('Unauthorized');
}
const client = new LumnisClient({ apiKey: LUMNISAI_API_KEY });
// Poll LumnisAI with 10s wait
const data = await client.responses.get(responseId, { wait: 10 });
// Update your database
// await database.update('agent_responses', {
// status: data.status,
// progress: data.progress ?? [],
// output_text: data.outputText ?? null,
// error: data.error ?? null,
// }).where('response_id', responseId);
return new Response(JSON.stringify(data), {
status: 200,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
} catch (error) {
console.error('Error in poll-response:', error);
const message = error instanceof Error ? error.message : 'Unknown error';
return new Response(JSON.stringify({ error: message }), {
status: 500,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
}
}
Create a user authentication flow:
1. Sign up/login page with auth
2. After successful auth, ensure Lumnis AI user:
const ensureUser = async (email, firstName, lastName) => {
const response = await fetch('/api/ensure-user', {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ email, firstName, lastName })
})
return response.json()
}
3. User creation logic (handled by edge function):
- Validates input with zod schema
- Ensures user.email matches authenticated user
- Tries getUser(email) first
- If 404 or "not found", creates with createUser()
- Returns status 200 for existing, 201 for new user
4. Redirect to dashboard after successful user creation/verification
Create app connection components for integrations:
1. Check connection status:
const checkConnectionStatus = async (appName) => {
const response = await fetch('/api/check-connection', {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ appName }) // e.g., 'googledocs', 'googlesheets', 'slack', etc.
})
return response.json()
}
2. Initiate app connection:
const initiateConnection = async (appName) => {
const response = await fetch('/api/initiate-connection', {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ appName })
})
const data = await response.json()
if (data.redirectUrl) {
window.open(data.redirectUrl, '_blank')
// Poll for status updates
const interval = setInterval(async () => {
const status = await checkConnectionStatus(appName)
if (status.status === 'active') {
clearInterval(interval)
setConnectionStatus(appName, 'active')
}
}, 5000)
}
}
3. Create UI components:
- Display each app integration as a card
- Show connection status for each app
- Display "Connected" (green checkmark) or "Connect" button (primary)
- Examples: Google Docs, Google Sheets, Slack, Gmail, etc.
- Use app icons for better visual recognition
4. Layout integrations in a responsive grid or card layout
If it doesn't exist yet, then create a simple form for users to submit tasks to the AI agent:
1. Main form component:
- Large textarea for task/prompt input
- Character count (optional)
- Examples or placeholder text to guide users
- Submit button with loading state
Example placeholder:
"Describe what you'd like the AI agent to do. For example:
- Research the top 5 CRM tools and create a comparison doc
- Analyze sales data and create a summary report
- Draft a project proposal based on these requirements..."
2. Submission handler:
const handleSubmit = async () => {
// Validate task prompt
if (!taskPrompt.trim()) {
showError('Please enter a task description')
return
}
// Optional: Check if required integrations are connected
const checkConnection = async (appName) => {
const response = await fetch('/api/check-connection', {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ appName })
})
return response.json()
}
// Example: Check if Google Docs is connected (if required for your use case)
const docsStatus = await checkConnection('googledocs')
if (docsStatus.status !== 'active') {
showWarning('Note: Some integrations are not connected. The agent will work with available tools.')
}
// Create agent response
const response = await fetch('/api/create-response', {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
taskPrompt: taskPrompt.trim()
})
})
const { responseId } = await response.json()
// Navigate to progress page
navigate(`/agent/${responseId}`)
}
3. UI Elements:
- Clear, prominent textarea (min 3-4 rows)
- Submit button: "Start Agent" or "Run Task"
- Show loading spinner during submission
- Display connection status badges for integrations (optional)
- Include examples or suggestions to help users get started
Create a real-time progress viewer for agent runs:
1. Polling implementation:
const pollResponse = async (responseId) => {
let done = false
while (!done) {
const response = await fetch('/api/poll-response', {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ responseId })
})
const data = await response.json()
// Update UI with new data
setStatus(data.status)
setProgress(data.progress || [])
setOutputText(data.outputText)
setError(data.error)
// Check if complete
done = ['succeeded', 'failed', 'cancelled'].includes(data.status)
if (!done) {
await new Promise(resolve => setTimeout(resolve, 2000))
}
}
}
2. Progress entry display:
data.progress?.forEach(entry => {
// Display each progress entry with:
// - Timestamp (entry.ts)
// - State (entry.state)
// - Message (entry.message)
// - Tool calls (entry.toolCalls) if present
// Format tool calls with arguments
if (entry.toolCalls) {
entry.toolCalls.forEach(tool => {
// Display tool name: tool.name
// Display tool arguments
if (tool.args) {
Object.entries(tool.args).forEach(([key, value]) => {
let displayValue = value
// Format arrays/lists properly
if (Array.isArray(value)) {
displayValue = `[${value.join(', ')}]`
}
// Format objects as JSON
if (typeof value === 'object' && !Array.isArray(value)) {
displayValue = JSON.stringify(value, null, 2)
}
// Truncate long values (>100 chars)
const stringValue = String(displayValue)
if (stringValue.length > 100) {
displayValue = stringValue.substring(0, 100) + '...'
}
// Display: key: displayValue
})
}
})
}
})
3. Status indicators:
- 'queued': Show "Waiting to start..."
- 'running': Show spinner/progress animation
- 'succeeded': Show success checkmark and render outputText as Markdown
- 'failed': Show error message
- 'cancelled': Show cancelled state
4. Final output handling:
- When status === 'succeeded', render data.outputText as Markdown
- Make all links clickable (Google Docs/Sheets links)
- Format tables and lists properly
- Allow collapsing/expanding progress entries
5. Error recovery:
- If failed, show error message from data.error
- Provide "Try Again" button to create new response
- Include "Back to Form" navigation
Improve the UX by:
1. Adding loading states for all async operations
2. Showing success toasts when operations complete
3. Adding proper error handling with user-friendly messages
4. Making the UI responsive and mobile-friendly
5. Adding animations for state transitions
6. Using a modern color scheme with good contrast
7. Remove placeholder or fake data.
8. Use thread title instead of task id in list of past tasks.
Step 2: Add API Key to Environment
LUMNISAI_API_KEY=api-key-here
Paste your actual Lumnis AI API key in place of api-key-here.
Step 3: Double-Check Production Implementation
Double-check that the following code implementation is complete and wired for production.
Verify that the lumnisai npm package is installed:
- Package should be in package.json dependencies
- Run: npm list lumnisai or pnpm list lumnisai to confirm
Verify database schema is created with these exact tables:
1. profiles table:
- id (UUID, primary key)
- user_id (UUID, unique)
- email (text)
- first_name (text)
- last_name (text)
- created_at, updated_at (timestamps)
2. agent_responses table:
- id (UUID, primary key)
- user_id (UUID)
- response_id (text, unique)
- status (text)
- progress (JSONB)
- output_text (text)
- error (text)
- created_at, updated_at (timestamps)
Verify RLS policies are enabled on both tables.
Verify update_updated_at_column function and triggers exist.
Verify ALL backend functions are deployed and have this EXACT implementation:
1. VERIFY ensure-user FUNCTION:
import { LumnisClient } from 'lumnisai';
import { z } from "zod";
const userInputSchema = z.object({
email: z.string().email().max(255),
firstName: z.string().max(100).optional(),
lastName: z.string().max(100).optional(),
});
const LUMNISAI_API_KEY = process.env.LUMNISAI_API_KEY;
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
};
async function ensureUser(req) {
if (req.method === 'OPTIONS') {
return new Response(null, { headers: corsHeaders });
}
try {
const authHeader = req.headers.get('Authorization');
if (!authHeader) {
return new Response(JSON.stringify({ error: 'Unauthorized' }), {
status: 401,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
}
const token = authHeader.replace('Bearer ', '');
// PRODUCTION CHECK: Replace with your actual auth verification
// const user = await getAuthenticatedUser(token);
if (!user) {
return new Response(JSON.stringify({ error: 'Unauthorized' }), {
status: 401,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
}
const body = await req.json();
const validation = userInputSchema.safeParse(body);
if (!validation.success) {
return new Response(JSON.stringify({ error: 'Invalid input data' }), {
status: 400,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
}
const { email, firstName, lastName } = validation.data;
if (user.email !== email) {
return new Response(JSON.stringify({ error: 'Forbidden' }), {
status: 403,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
}
if (!LUMNISAI_API_KEY) {
throw new Error('LUMNISAI_API_KEY not configured');
}
const client = new LumnisClient({ apiKey: LUMNISAI_API_KEY });
try {
const existing = await client.getUser(email);
console.log('User exists:', email);
return new Response(JSON.stringify(existing), {
status: 200,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
} catch (error) {
if (error.message?.includes('not found') || error.response?.status === 404) {
const created = await client.createUser({
email,
firstName: firstName ?? email.split('@')[0],
lastName: lastName ?? 'User',
});
console.log('User created:', email);
return new Response(JSON.stringify(created), {
status: 201,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
}
throw error;
}
} catch (error) {
console.error('Error in ensure-user:', error);
const message = error instanceof Error ? error.message : 'Unknown error';
return new Response(JSON.stringify({ error: message }), {
status: 500,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
}
}
2. VERIFY initiate-connection FUNCTION:
import { LumnisClient } from 'lumnisai';
const LUMNISAI_API_KEY = process.env.LUMNISAI_API_KEY;
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
};
async function initiateConnection(req) {
if (req.method === 'OPTIONS') {
return new Response(null, { headers: corsHeaders });
}
try {
const authHeader = req.headers.get('Authorization');
if (!authHeader) {
throw new Error('No authorization header');
}
if (!LUMNISAI_API_KEY) {
throw new Error('LUMNISAI_API_KEY not configured');
}
const token = authHeader.replace('Bearer ', '');
// PRODUCTION CHECK: Replace with your actual auth verification
// const user = await getAuthenticatedUser(token);
if (!user?.email) {
throw new Error('Unauthorized');
}
const { appName } = await req.json();
console.log(`Initiating ${appName} connection for ${user.email}`);
const client = new LumnisClient({ apiKey: LUMNISAI_API_KEY });
const appEnabled = await client.isAppEnabled(appName);
if (!appEnabled.enabled) {
console.log(`Enabling ${appName} at tenant level...`);
await client.setAppEnabled(appName, true);
}
const connection = await client.initiateConnection({
userId: user.email,
appName: appName,
});
console.log(`${appName} connection initiated for ${user.email}:`, connection);
return new Response(JSON.stringify({
redirectUrl: connection.redirectUrl,
status: 'pending'
}), {
status: 200,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
} catch (error) {
console.error('Error connecting:', error);
const message = error instanceof Error ? error.message : 'Unknown error';
return new Response(JSON.stringify({ error: message }), {
status: 500,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
}
}
3. VERIFY check-connection FUNCTION:
import { LumnisClient } from 'lumnisai';
const LUMNISAI_API_KEY = process.env.LUMNISAI_API_KEY;
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
};
async function checkConnection(req) {
if (req.method === 'OPTIONS') {
return new Response(null, { headers: corsHeaders });
}
try {
const authHeader = req.headers.get('Authorization');
if (!authHeader) {
throw new Error('No authorization header');
}
if (!LUMNISAI_API_KEY) {
throw new Error('LUMNISAI_API_KEY not configured');
}
const token = authHeader.replace('Bearer ', '');
// PRODUCTION CHECK: Replace with your actual auth verification
// const user = await getAuthenticatedUser(token);
if (!user?.email) {
throw new Error('Unauthorized');
}
const { appName } = await req.json();
console.log(`Checking ${appName} connection status for ${user.email}`);
const client = new LumnisClient({ apiKey: LUMNISAI_API_KEY });
const status = await client.getConnectionStatus(user.email, appName);
console.log(`${appName} status for ${user.email}:`, status);
return new Response(JSON.stringify(status), {
status: 200,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
} catch (error) {
console.error('Error checking status:', error);
const message = error instanceof Error ? error.message : 'Unknown error';
return new Response(JSON.stringify({ error: message }), {
status: 500,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
}
}
4. VERIFY create-response FUNCTION:
import { LumnisClient } from "lumnisai";
// PRODUCTION CHECK: Customize this prompt for your specific use case
const AGENT_PROMPT = `You are an expert AI agent with access to various tools and integrations. Your role is to help users accomplish their tasks efficiently and accurately.
Key capabilities:
- Research and gather information from the web
- Create and edit documents in Google Docs
- Manage data in Google Sheets
- Integrate with various productivity tools
- Provide comprehensive, well-structured outputs
Guidelines:
- Break down complex tasks into manageable steps
- Use appropriate tools for each subtask
- Verify information when possible
- Provide clear explanations of your actions
- Ask for clarification if the task is ambiguous`;
const LUMNISAI_API_KEY = process.env.LUMNISAI_API_KEY;
const corsHeaders = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",
};
async function createResponse(req) {
if (req.method === "OPTIONS") {
return new Response(null, { headers: corsHeaders });
}
try {
const authHeader = req.headers.get("Authorization");
if (!authHeader) {
throw new Error("No authorization header");
}
const token = authHeader.replace("Bearer ", "");
// PRODUCTION CHECK: Replace with your actual auth verification
// const user = await getAuthenticatedUser(token);
if (!user) {
throw new Error("Unauthorized");
}
const body = await req.json();
const { taskPrompt } = body;
if (!LUMNISAI_API_KEY) {
throw new Error("LUMNISAI_API_KEY not configured");
}
const client = new LumnisClient({ apiKey: LUMNISAI_API_KEY });
const taskMessage = `${AGENT_PROMPT}\n\nUSER TASK:\n${taskPrompt}`;
const responseData = await client.responses.create({
messages: [{ role: "user", content: taskMessage }],
userId: user.email,
agentConfig: {
coordinatorModelName: "openai:gpt-4.1",
plannerModelName: "openai:gpt-4.1",
orchestratorModelName: "openai:gpt-4.1",
useCognitiveTools: true,
enableTaskValidation: true,
generateComprehensiveOutput: false,
},
});
const responseId = responseData.responseId;
// PRODUCTION CHECK: Store in database
// await database.insert('agent_responses', {
// user_id: user.id,
// response_id: responseId,
// status: "queued",
// });
console.log("Response created:", responseId);
console.log("User:", user.email);
return new Response(JSON.stringify({ responseId }), {
status: 201,
headers: { ...corsHeaders, "Content-Type": "application/json" },
});
} catch (error) {
console.error("Error in create-response:", error);
const message = error instanceof Error ? error.message : "Unknown error";
return new Response(JSON.stringify({ error: message }), {
status: 500,
headers: { ...corsHeaders, "Content-Type": "application/json" },
});
}
}
5. VERIFY poll-response FUNCTION:
import { LumnisClient } from 'lumnisai';
const LUMNISAI_API_KEY = process.env.LUMNISAI_API_KEY;
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
};
async function pollResponse(req) {
if (req.method === 'OPTIONS') {
return new Response(null, { headers: corsHeaders });
}
try {
const { responseId } = await req.json();
if (!responseId) {
throw new Error('Missing responseId');
}
const authHeader = req.headers.get('Authorization');
if (!authHeader) {
throw new Error('No authorization header');
}
if (!LUMNISAI_API_KEY) {
throw new Error('LUMNISAI_API_KEY not configured');
}
const token = authHeader.replace('Bearer ', '');
// PRODUCTION CHECK: Replace with your actual auth verification
// const user = await getAuthenticatedUser(token);
if (!user) {
throw new Error('Unauthorized');
}
const client = new LumnisClient({ apiKey: LUMNISAI_API_KEY });
const data = await client.responses.get(responseId, { wait: 10 });
// PRODUCTION CHECK: Update database
// await database.update('agent_responses', {
// status: data.status,
// progress: data.progress ?? [],
// output_text: data.outputText ?? null,
// error: data.error ?? null,
// }).where('response_id', responseId);
return new Response(JSON.stringify(data), {
status: 200,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
} catch (error) {
console.error('Error in poll-response:', error);
const message = error instanceof Error ? error.message : 'Unknown error';
return new Response(JSON.stringify({ error: message }), {
status: 500,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
}
}
PRODUCTION CHECKLIST:
✅ Demo Mode Check:
- [ ] VERIFY THE SYSTEM IS NOT IN DEMO MODE
- [ ] Confirm all backend functions are using real Lumnis AI API calls
- [ ] Ensure no mock/fake data is being returned
✅ Available App Connections:
- [ ] Verify these are the available apps to connect:
const apps_to_connect = ["github", "gmail", "googledocs", "googlesheets", "googledrive", "outlook"]
- [ ] Ensure appName parameter only accepts these values
- [ ] Test at least one connection from each category (docs, email, code)
- [ ] CRITICAL: Anywhere integrations appear in the UI, checkConnectionStatus() must be called
- [ ] Never assume an integration is connected - always verify with the check-connection endpoint
✅ Environment Variables:
- [ ] LUMNISAI_API_KEY is set in production environment
- [ ] API key has correct permissions for all operations
✅ Authentication:
- [ ] Replace all // const user = await getAuthenticatedUser(token); comments with actual auth implementation
- [ ] Verify user.email is correctly populated from your auth system
- [ ] Ensure token validation is properly implemented
- [ ] CRITICAL: When user signs up, ALWAYS invoke ensure-user function immediately after successful authentication
- [ ] ensure-user must be called before user can access any agent features
- [ ] Store the Lumnis user creation status in your database for tracking
✅ Database Operations:
- [ ] Uncomment and implement all database insert/update operations
- [ ] Verify database connection is established
- [ ] Test that RLS policies work correctly
- [ ] Ensure timestamps are auto-updating
✅ CORS Configuration:
- [ ] Update Access-Control-Allow-Origin from '*' to your actual domain(s)
- [ ] Configure proper CORS headers for production
✅ Error Handling:
- [ ] All functions have try-catch blocks
- [ ] Error messages are logged but not exposed to client in detail
- [ ] Implement proper error monitoring/alerting
✅ Agent Configuration:
- [ ] Customize AGENT_PROMPT for your specific use case
- [ ] Configure appropriate model names (coordinatorModelName, plannerModelName, orchestratorModelName)
- [ ] Test with your expected workload
✅ API Endpoints:
- [ ] All 5 endpoints are deployed and accessible:
- /api/ensure-user (POST)
- /api/initiate-connection (POST)
- /api/check-connection (POST)
- /api/create-response (POST)
- /api/poll-response (POST)
✅ Frontend Integration:
- [ ] All API calls include proper Authorization headers
- [ ] Error states are handled gracefully
- [ ] Loading states are shown during async operations
- [ ] Connection status polling works correctly
- [ ] Progress updates display in real-time
- [ ] ANYWHERE integrations are displayed, connection status is checked using checkConnectionStatus()
- [ ] Integration cards/UI elements show real-time connection status (Connected/Not Connected)
- [ ] Before submitting tasks, optionally check if required integrations are connected
- [ ] Display warning if user tries to use features requiring unconnected integrations
✅ Testing:
- [ ] Test user creation flow end-to-end
- [ ] VERIFY: New user signup → ensure-user called → Lumnis user created → Dashboard access
- [ ] Test at least one app connection (e.g., Google Docs)
- [ ] Submit a test task and verify it completes
- [ ] Verify progress updates appear in real-time
- [ ] Test error scenarios (invalid input, network failures)
✅ Security:
- [ ] API key is never exposed to frontend
- [ ] User can only access their own data
- [ ] Input validation is working (zod schemas)
- [ ] Rate limiting is implemented if needed
✅ Performance:
- [ ] Long-polling timeout (10s) is appropriate
- [ ] Database queries are optimized
- [ ] Consider implementing caching if needed
If any item is not checked, fix it before going to production!
Important Notes
- All backend endpoints use POST method (even for checking status)
- Pass appName in request body for app connections (e.g., 'googledocs', 'googlesheets', 'slack', etc.)
- responseId goes in request body for polling, not query params
- Always include Authorization header with Bearer token
- LUMNISAI_API_KEY must be available in the environment
Tips for Success
- Always provide your actual use case - Replace generic examples with your specific needs
- Test incrementally - Verify each step works before moving to the next
- Check logs - Use the logs to debug backend issues
- Be specific about UI - Describe exactly how you want the interface to look
- Save response IDs - Always store them in the database for persistence
- Use proper TypeScript types - Import types like
AgentConfig,ApiProviderfrom lumnisai
Troubleshooting
If agent doesn't start:
Check that:
1. User exists in Lumnis AI (call ensure-user first)
2. API key is correctly set in environment
3. Task data is properly formatted as JSON
4. AgentConfig includes all required model names
If app connection/OAuth fails:
Ensure:
1. Apps are enabled at tenant level with setAppEnabled()
2. User email matches the authenticated user
3. OAuth redirect URL completes fully
4. Connection status transitions from 'pending' to 'active'
5. appName is correctly formatted (e.g., 'googledocs', 'slack', etc.)
If progress doesn't update:
Verify:
1. responseId is being passed correctly
2. Using { wait: 10 } for long-polling
3. Progress array is being properly parsed
4. Status checks include all terminal states: ['succeeded', 'failed', 'cancelled']
Common Backend Issues:
If DNS resolution fails:
- Check that LUMNISAI_API_KEY is available in environment
- Add try-catch blocks with detailed error logging
If types are missing:
- Import types explicitly: import { type AgentConfig } from 'lumnisai'
- Use proper TypeScript configuration
Summary
You now have a complete guide to integrate Lumnis AI with TrySolid! This integration enables:
- AI Agent Capabilities: Research, document creation, data analysis, and more
- App Integrations: Connect to Google Workspace, Slack, and other productivity tools
- Real-time Progress Tracking: Live updates as the agent works
- User Management: Automatic user creation and profile management
- Flexible Configuration: Customizable models and agent settings
Remember to:
- Install the
lumnisainpm package - Set up your API key properly
- Test each component incrementally
- Use the example code as templates
- Check the logs if you encounter issues
Customizing Your Integration
The examples in this guide provide a general-purpose AI agent. You can customize the integration to match your specific use case:
Customizing the Agent Prompt
In the backend functionality for creating responses, modify the AGENT_PROMPT constant to give your agent specific instructions, domain knowledge, or personality:
const AGENT_PROMPT = `You are a marketing specialist AI agent...
- Focus on SEO and content marketing
- Create engaging, conversion-focused content
- Follow brand guidelines for [Your Brand]
...`;Customizing the Input Form
Instead of a simple textarea, you can create structured forms with specific fields:
// Example: Event planning form
const formData = {
eventName: string,
date: date,
budget: number,
requirements: string[]
}
// Pass to agent as JSON
body: JSON.stringify({
taskPrompt: `Plan an event with these details:\n${JSON.stringify(formData, null, 2)}`
})Or keep it simple with a prompt-based approach that lets users describe their needs in natural language.
Tip: Start with the simple prompt-based form, gather user feedback, and add structure only where it provides clear value.
For more details on the Lumnis AI SDK, refer to the JavaScript SDK documentation.