What You’ll Build
A customer support chatbot that:- Answers questions from your documentation
- Maintains conversation context
- Streams responses in real-time
- Displays source citations
Prerequisites: Cuadra AI account, Node.js 18+
Step 1: Create a Model
Create an AI model via the Dashboard or API. First, pick a base model from the catalog, then create your custom model. For API access, authenticate via M2M OAuth 2.0:# Get a parent model ID from the catalog
curl https://api.cuadra.ai/v1/models/catalog \
-H "Authorization: Bearer $ACCESS_TOKEN"
# Create your model
curl -X POST https://api.cuadra.ai/v1/models \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: create-model-001" \
-d '{
"parentModelId": "PARENT_MODEL_ID_FROM_CATALOG",
"displayName": "Support Bot"
}'
import httpx
# Get a parent model ID from the catalog
catalog = httpx.get(
"https://api.cuadra.ai/v1/models/catalog",
headers={"Authorization": f"Bearer {ACCESS_TOKEN}"}
).json()
parent_id = catalog["items"][0]["id"]
# Create your model
response = httpx.post(
"https://api.cuadra.ai/v1/models",
headers={
"Authorization": f"Bearer {ACCESS_TOKEN}",
"Idempotency-Key": "create-model-001"
},
json={"parentModelId": parent_id, "displayName": "Support Bot"}
)
model = response.json()
print(f"Created model: {model['id']}")
// Get a parent model ID from the catalog
const catalogRes = await fetch('https://api.cuadra.ai/v1/models/catalog', {
headers: { 'Authorization': `Bearer ${ACCESS_TOKEN}` }
});
const catalog = await catalogRes.json();
const parentId = catalog.items[0].id;
// Create your model
const response = await fetch('https://api.cuadra.ai/v1/models', {
method: 'POST',
headers: {
'Authorization': `Bearer ${ACCESS_TOKEN}`,
'Content-Type': 'application/json',
'Idempotency-Key': 'create-model-001'
},
body: JSON.stringify({
parentModelId: parentId,
displayName: 'Support Bot'
})
});
const model = await response.json();
console.log(`Created model: ${model.id}`);
id (e.g., model_abc123).
Step 2: Create a Knowledge Base
Upload your documentation:Create Dataset
curl -X POST https://api.cuadra.ai/v1/datasets \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: create-dataset-001" \
-d '{"name": "Product Docs"}'
import httpx
response = httpx.post(
"https://api.cuadra.ai/v1/datasets",
headers={
"Authorization": f"Bearer {ACCESS_TOKEN}",
"Idempotency-Key": "create-dataset-001"
},
json={"name": "Product Docs"}
)
dataset = response.json()
print(f"Created dataset: {dataset['id']}")
const response = await fetch('https://api.cuadra.ai/v1/datasets', {
method: 'POST',
headers: {
'Authorization': `Bearer ${ACCESS_TOKEN}`,
'Content-Type': 'application/json',
'Idempotency-Key': 'create-dataset-001'
},
body: JSON.stringify({ name: 'Product Docs' })
});
const dataset = await response.json();
console.log(`Created dataset: ${dataset.id}`);
Upload Documents
Upload files and associate them with the dataset:# Step 1: Upload the file
FILE_RESPONSE=$(curl -X POST https://api.cuadra.ai/v1/files \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Idempotency-Key: upload-doc-001" \
-F "file=@docs/getting-started.pdf")
FILE_ID=$(echo $FILE_RESPONSE | jq -r '.id')
# Step 2: Associate with dataset
curl -X POST https://api.cuadra.ai/v1/files/$FILE_ID/associations \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"datasetId": "ds_xyz"}'
import httpx
# Step 1: Upload the file
with open("docs/getting-started.pdf", "rb") as f:
file_response = httpx.post(
"https://api.cuadra.ai/v1/files",
headers={
"Authorization": f"Bearer {ACCESS_TOKEN}",
"Idempotency-Key": "upload-doc-001"
},
files={"file": f}
)
file = file_response.json()
print(f"Uploaded file: {file['id']}")
# Step 2: Associate with dataset
httpx.post(
f"https://api.cuadra.ai/v1/files/{file['id']}/associations",
headers={"Authorization": f"Bearer {ACCESS_TOKEN}"},
json={"datasetId": "ds_xyz"}
)
print("File added to dataset")
import fs from 'fs';
import FormData from 'form-data';
// Step 1: Upload the file
const formData = new FormData();
formData.append('file', fs.createReadStream('docs/getting-started.pdf'));
const fileResponse = await fetch('https://api.cuadra.ai/v1/files', {
method: 'POST',
headers: {
'Authorization': `Bearer ${ACCESS_TOKEN}`,
'Idempotency-Key': 'upload-doc-001'
},
body: formData
});
const file = await fileResponse.json();
console.log(`Uploaded file: ${file.id}`);
// Step 2: Associate with dataset
await fetch(`https://api.cuadra.ai/v1/files/${file.id}/associations`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${ACCESS_TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ datasetId: 'ds_xyz' })
});
console.log('File added to dataset');
Step 3: Link Dataset to Model
Connect your knowledge base:curl -X POST https://api.cuadra.ai/v1/models/model_abc123/datasets \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: link-dataset-001" \
-d '{"datasetId": "ds_xyz", "usageType": "rag"}'
import httpx
response = httpx.post(
"https://api.cuadra.ai/v1/models/model_abc123/datasets",
headers={
"Authorization": f"Bearer {ACCESS_TOKEN}",
"Idempotency-Key": "link-dataset-001"
},
json={"datasetId": "ds_xyz", "usageType": "rag"}
)
print("Dataset linked to model")
const response = await fetch('https://api.cuadra.ai/v1/models/model_abc123/datasets', {
method: 'POST',
headers: {
'Authorization': `Bearer ${ACCESS_TOKEN}`,
'Content-Type': 'application/json',
'Idempotency-Key': 'link-dataset-001'
},
body: JSON.stringify({ datasetId: 'ds_xyz', usageType: 'rag' })
});
console.log('Dataset linked to model');
Step 4: Add System Prompt
Create a particle for the bot’s behavior:curl -X POST https://api.cuadra.ai/v1/particles \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: create-particle-001" \
-d '{
"name": "Support Role",
"category": "role",
"content": "You are a helpful support agent. Answer questions based on the provided documentation. If unsure, say so and suggest contacting support@example.com."
}'
import httpx
response = httpx.post(
"https://api.cuadra.ai/v1/particles",
headers={
"Authorization": f"Bearer {ACCESS_TOKEN}",
"Idempotency-Key": "create-particle-001"
},
json={
"name": "Support Role",
"category": "role",
"content": "You are a helpful support agent. Answer questions based on the provided documentation. If unsure, say so and suggest contacting support@example.com."
}
)
particle = response.json()
print(f"Created particle: {particle['id']}")
const response = await fetch('https://api.cuadra.ai/v1/particles', {
method: 'POST',
headers: {
'Authorization': `Bearer ${ACCESS_TOKEN}`,
'Content-Type': 'application/json',
'Idempotency-Key': 'create-particle-001'
},
body: JSON.stringify({
name: 'Support Role',
category: 'role',
content: 'You are a helpful support agent. Answer questions based on the provided documentation. If unsure, say so and suggest contacting support@example.com.'
})
});
const particle = await response.json();
console.log(`Created particle: ${particle.id}`);
Create System Prompt
curl -X POST https://api.cuadra.ai/v1/system-prompts \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: create-sysprompt-001" \
-d '{
"name": "Support Prompt",
"particles": [{"particleId": "particle_xxx", "order": 1}]
}'
import httpx
response = httpx.post(
"https://api.cuadra.ai/v1/system-prompts",
headers={
"Authorization": f"Bearer {ACCESS_TOKEN}",
"Idempotency-Key": "create-sysprompt-001"
},
json={
"name": "Support Prompt",
"particles": [{"particleId": "particle_xxx", "order": 1}]
}
)
system_prompt = response.json()
print(f"Created system prompt: {system_prompt['id']}")
const response = await fetch('https://api.cuadra.ai/v1/system-prompts', {
method: 'POST',
headers: {
'Authorization': `Bearer ${ACCESS_TOKEN}`,
'Content-Type': 'application/json',
'Idempotency-Key': 'create-sysprompt-001'
},
body: JSON.stringify({
name: 'Support Prompt',
particles: [{ particleId: 'particle_xxx', order: 1 }]
})
});
const systemPrompt = await response.json();
console.log(`Created system prompt: ${systemPrompt.id}`);
Attach to Model
curl -X PATCH https://api.cuadra.ai/v1/models/model_abc123 \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"systemPromptId": "sysprompt_yyy"}'
import httpx
response = httpx.patch(
"https://api.cuadra.ai/v1/models/model_abc123",
headers={"Authorization": f"Bearer {ACCESS_TOKEN}"},
json={"systemPromptId": "sysprompt_yyy"}
)
print("System prompt attached to model")
const response = await fetch('https://api.cuadra.ai/v1/models/model_abc123', {
method: 'PATCH',
headers: {
'Authorization': `Bearer ${ACCESS_TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ systemPromptId: 'sysprompt_yyy' })
});
console.log('System prompt attached to model');
Step 5: Test via API
Verify everything works:curl -X POST https://api.cuadra.ai/v1/chats \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: test-chat-001" \
-d '{
"modelId": "model_abc123",
"messages": [{"role": "user", "content": "How do I get started?"}]
}'
import httpx
response = httpx.post(
"https://api.cuadra.ai/v1/chats",
headers={
"Authorization": f"Bearer {ACCESS_TOKEN}",
"Idempotency-Key": "test-chat-001"
},
json={
"modelId": "model_abc123",
"messages": [{"role": "user", "content": "How do I get started?"}]
}
)
result = response.json()
print(f"Response: {result['message']['content']}")
# Display sources if available
if result.get('sources'):
print("\nSources:")
for source in result['sources']:
print(f" - {source['filename']} (score: {source['score']:.2f})")
const response = await fetch('https://api.cuadra.ai/v1/chats', {
method: 'POST',
headers: {
'Authorization': `Bearer ${ACCESS_TOKEN}`,
'Content-Type': 'application/json',
'Idempotency-Key': 'test-chat-001'
},
body: JSON.stringify({
modelId: 'model_abc123',
messages: [{ role: 'user', content: 'How do I get started?' }]
})
});
const result = await response.json();
console.log(`Response: ${result.message.content}`);
// Display sources if available
if (result.sources) {
console.log('\nSources:');
result.sources.forEach(source => {
console.log(` - ${source.filename} (score: ${source.score.toFixed(2)})`);
});
}
Step 6: Build the React UI
Install the Cuadra AI UI Kit:npm install @cuadra-ai/uikit
// src/components/SupportChat.tsx
import { CuadraChat } from '@cuadra-ai/uikit';
export function SupportChat({ sessionToken }: { sessionToken: string }) {
return (
<div style={{ height: '600px', width: '400px' }}>
<CuadraChat
connection={{
baseUrl: "https://api.cuadra.ai",
sessionToken: sessionToken
}}
chat={{
modelId: "model_abc123",
mode: "multiChat"
}}
/>
</div>
);
}
Step 7: User Authentication
Users authenticate via your Stytch B2B integration. The session token from Stytch is passed to the UI Kit.Frontend Integration
import { useState, useEffect } from 'react';
import { CuadraChat } from '@cuadra-ai/uikit';
export function App() {
// Session token from your Stytch B2B authentication
const [sessionToken, setSessionToken] = useState<string | null>(null);
useEffect(() => {
// Your auth system provides the session token after user login
const token = getStytchSessionToken(); // From your auth implementation
setSessionToken(token);
}, []);
if (!sessionToken) {
return <div>Please log in</div>;
}
return (
<CuadraChat
connection={{
baseUrl: "https://api.cuadra.ai",
sessionToken: sessionToken
}}
chat={{
modelId: "model_abc123",
mode: "multiChat"
}}
/>
);
}
Proxy Mode (Alternative)
If you prefer backend-handled auth, route requests through your backend: Frontend:<CuadraChat
connection={{
proxyUrl: "/api/chat" // Your backend handles auth
}}
chat={{
modelId: "model_abc123"
}}
/>
from fastapi import FastAPI, Request
import httpx
app = FastAPI()
@app.post("/api/chat")
async def proxy_chat(request: Request):
body = await request.json()
async with httpx.AsyncClient() as client:
response = await client.post(
"https://api.cuadra.ai/v1/chats",
headers={
"Authorization": f"Bearer {M2M_ACCESS_TOKEN}",
"Content-Type": "application/json",
},
json=body
)
return response.json()
const express = require('express');
const app = express();
app.use(express.json());
app.post('/api/chat', async (req, res) => {
const response = await fetch('https://api.cuadra.ai/v1/chats', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.M2M_ACCESS_TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(req.body)
});
const data = await response.json();
res.json(data);
});
Next Steps
Knowledge Bases
Add more data sources
System Prompts
Customize behavior
Streaming
Real-time responses
Usage
Track usage