Best Practices
Follow these recommendations to build a robust, secure, and performant integration with DocBit AI.
Security
Protect Your API Key
Never expose your API key in client-side code, mobile apps, or public repositories.
Do:
- Store in environment variables
- Use a secrets manager (AWS Secrets Manager, HashiCorp Vault)
- Proxy requests through your backend
Don’t:
- Hardcode in source code
- Include in frontend JavaScript
- Commit to version control
- Log in plain text
// Good: Environment variable
const apiKey = process.env.DOCBIT_API_KEY;
// Bad: Hardcoded
const apiKey = 'sk_yourpartner_abc123...'; // Never do this!
Server-Side Role Assignment
Always determine roles server-side based on your authenticated user:
// Secure: Derive roles from authenticated user
app.post('/api/ask', authMiddleware, async (req, res) => {
const roles = getRolesFromUser(req.user); // Server determines roles
const response = await DocBit AI.chat({
headers: {
'X-External-Roles': JSON.stringify(roles)
}
});
});
// Insecure: Never trust client-provided roles
app.post('/api/ask', async (req, res) => {
const roles = req.body.roles; // DANGEROUS!
});
Validate Organization Access
Ensure users can only access their authorized organizations:
app.post('/api/ask', authMiddleware, async (req, res) => {
const user = req.user;
const requestedOrgId = req.body.orgId;
// Verify user belongs to this org
if (!user.organizations.includes(requestedOrgId)) {
return res.status(403).json({ error: 'Access denied' });
}
// Proceed with request
});
Error Handling
Always Handle Errors
Never assume API calls succeed:
async function askQuestion(question) {
try {
const response = await DocBit AI.chat({ message: question });
return response;
} catch (error) {
if (error.response) {
// API returned an error
handleApiError(error.response.status, error.response.data);
} else if (error.request) {
// Network error
handleNetworkError(error);
} else {
// Other error
throw error;
}
}
}
Implement Retry Logic
Retry transient failures with exponential backoff:
async function withRetry(fn, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
const status = error.response?.status;
// Retry on rate limit or server errors
if (status === 429 || status >= 500) {
const waitTime = Math.pow(2, i) * 1000;
await sleep(waitTime);
continue;
}
// Don't retry client errors
throw error;
}
}
throw new Error('Max retries exceeded');
}
User-Friendly Error Messages
Don’t expose raw API errors to end users:
function getUserMessage(error) {
switch (error.response?.status) {
case 400:
return 'Please check your question and try again.';
case 401:
return 'Session expired. Please log in again.';
case 429:
return 'Too many requests. Please wait a moment.';
case 500:
return 'Service temporarily unavailable. Please try again.';
default:
return 'Something went wrong. Please try again.';
}
}
Use Streaming for Chat
Streaming provides better user experience for long responses:
// Better UX: Stream tokens as they arrive
async function streamResponse(question, onToken) {
const response = await fetch('/api/ai/chat/stream', {
method: 'POST',
body: JSON.stringify({ message: question })
});
const reader = response.body.getReader();
// Process tokens as they arrive...
}
Cache Conversation Context
Store conversation IDs to continue conversations efficiently:
class ConversationManager {
constructor() {
this.conversationId = null;
}
async ask(question) {
const response = await DocBit AI.chat({
message: question,
conversationId: this.conversationId
});
// Store for follow-ups
this.conversationId = response.conversationId;
return response;
}
}
Batch Document Uploads
When uploading multiple documents, pace them to avoid rate limits:
async function uploadBatch(files) {
const results = [];
for (const file of files) {
const result = await uploadDocument(file);
results.push(result);
// Pace uploads
await sleep(1000);
}
return results;
}
User Experience
Show Confidence Indicators
Display the confidence level to set user expectations:
function renderResponse(response) {
return (
<div>
<p>{response.content}</p>
<ConfidenceBadge level={response.confidence} />
{response.confidence === 'Low' && (
<Warning>This answer may be incomplete. Check the sources.</Warning>
)}
</div>
);
}
Display Citations
Help users verify answers by showing sources:
function renderCitations(citations) {
return (
<div className="citations">
<h4>Sources</h4>
{citations.map(citation => (
<Citation key={citation.documentId}>
<strong>{citation.documentTitle}</strong>
{citation.page && <span> - Page {citation.page}</span>}
<blockquote>{citation.excerpt}</blockquote>
</Citation>
))}
</div>
);
}
Handle Loading States
Show progress while waiting for responses:
function ChatInterface() {
const [loading, setLoading] = useState(false);
const [streaming, setStreaming] = useState(false);
return (
<div>
{loading && !streaming && <LoadingSpinner />}
{streaming && <TypingIndicator />}
<MessageList messages={messages} />
</div>
);
}
Testing
Use a Sandbox Organization
Create a test organization for development:
const config = {
development: {
orgId: 'test-sandbox',
apiUrl: 'https://api.docbit.ai'
},
production: {
orgId: process.env.ORG_ID,
apiUrl: 'https://api.docbit.ai'
}
};
Test Role Combinations
Verify access control with different roles:
describe('Access Control', () => {
it('HR user sees HR documents', async () => {
const response = await askWithRoles(['hr'], 'What are the HR policies?');
expect(response.citations).toContain(hrDocument);
});
it('Finance user does not see HR documents', async () => {
const response = await askWithRoles(['finance'], 'What are the HR policies?');
expect(response.citations).not.toContain(hrDocument);
});
});
Test Error Scenarios
Verify error handling works correctly:
describe('Error Handling', () => {
it('handles rate limits gracefully', async () => {
// Simulate 429 response
const response = await askWithRetry(question);
expect(response).toBeDefined();
});
it('shows user-friendly error messages', async () => {
const errorMessage = getUserMessage({ response: { status: 429 } });
expect(errorMessage).not.toContain('429');
});
});
Logging and Monitoring
Log API Interactions
Log requests for debugging (but not sensitive data):
function logRequest(method, endpoint, orgId, userId) {
console.log({
timestamp: new Date().toISOString(),
method,
endpoint,
orgId,
userId: hashUserId(userId), // Don't log raw user IDs
// Don't log: API key, message content, roles
});
}
Track Key Metrics
Monitor important metrics:
- Response times
- Error rates
- Rate limit hits
- Confidence distribution
- Citation counts
class MetricsTracker {
track(response, duration) {
metrics.histogram('api.response_time', duration);
metrics.increment('api.requests');
metrics.increment(`api.confidence.${response.confidence.toLowerCase()}`);
}
}
Checklist
Before going to production: