Developer Documentation
Everything you need to integrate Citai into your website or application.
1 Quick Start
1. Create an API Key
Go to Settings > API Keys in your dashboard and create a new key. Save it securely, it's only shown once.
2. Copy the snippet
Paste this code before the closing </body> tag in your HTML. Replace YOUR_API_KEY with your actual key.
<script
src="https://citai.ai/widget.js"
data-api-key="sk-YOUR_API_KEY"
data-base-url="https://citai.ai"
data-lang="EN">
</script>3. Done
The widget will appear as a floating bubble on your site. Visitors can chat with your knowledge base instantly.
2 Widget Configuration
All options are configured from Dashboard > API Keys > Edit. The widget loads them automatically from the server.
Snippet Attributes (HTML)
| Field | Type | Default | Description |
|---|---|---|---|
| data-api-key | string | — | Your API key (required). Identifies the tenant and widget configuration |
| data-base-url | string | — | Base URL of your Citai instance (required) |
| data-lang | string | (auto) | Forces the widget language (ES, EN, PT, DE, FR, IT). If omitted, detects from HTML lang attribute, localStorage or browser |
| data-theme | string | (server) | Widget visual theme (light / dark). If omitted, uses server configuration |
| data-position | string | "bottom-right" | Bubble position (bottom-right / bottom-left) |
Appearance
| Field | Type | Default | Description |
|---|---|---|---|
| theme | string | "light" | Widget theme (light / dark) |
| position | string | "bottom-right" | Screen position (bottom-right / bottom-left) |
| primary_color | string | "#2563eb" | Widget primary color (hex) |
| logo_url | string | null | null | Logo URL shown in the header |
| powered_by | boolean | true | Show 'Powered by Citai' in the footer |
Messages
| Field | Type | Default | Description |
|---|---|---|---|
| agent_name | string | null | null | Agent name displayed in the header |
| avatar_url | string | null | null | Agent avatar URL |
| welcome_message | string | "Hola! ..." | Initial welcome message |
| welcome_messages_i18n | object | null | null | Welcome messages by language (e.g. ES, EN, PT as keys) |
| farewell_message | string | null | "Gracias..." | Farewell message when conversation ends |
| out_of_scope_message | string | null | null | Response when the question is out of scope |
Interaction
| Field | Type | Default | Description |
|---|---|---|---|
| conversation_starters | string[] | [] | Initial suggestions as clickable chips |
| conversation_starters_i18n | object | null | null | Initial suggestions by language |
| quick_replies_templates | string[] | [] | Quick reply templates |
| pre_chat_fields | PreChatField[] | [] | Pre-chat form fields (name, email, etc.) |
| proactive_triggers | ProactiveTrigger[] | [] | Proactive triggers: by time, scroll or URL |
| show_sources | boolean | true | Show sources and citations in responses |
| rating_enabled | boolean | true | Allow visitors to rate responses |
| inactivity_timeout_seconds | integer | 300 | Seconds of inactivity before timeout |
Escalation
| Field | Type | Default | Description |
|---|---|---|---|
| escalation_config.enabled | boolean | false | Enable escalation to human agent |
| escalation_config.webhook_url | string | null | null | Webhook URL for receiving escalations |
| escalation_config.email | string | null | null | Email for escalation notifications |
| escalation_config.confidence_threshold | float | 0.3 | Minimum confidence for auto-escalation (0.0 - 1.0) |
| escalation_config.message | string | "" | Message shown to user on escalation |
Business Hours
| Field | Type | Default | Description |
|---|---|---|---|
| business_hours.enabled | boolean | false | Enable business hours |
| business_hours.timezone | string | "UTC" | Timezone (e.g., America/New_York) |
| business_hours.schedule | Schedule[] | [] | Schedule by day (array with day, start, end) |
| business_hours.out_of_hours_message | string | "" | Out of hours message |
| business_hours.ooh_capture_enabled | boolean | false | Allow data capture outside business hours |
3 Webhooks
Receive real-time notifications when events occur in your conversations. Configure them in Dashboard > Webhooks.
Headers on each request
Retries: 3 additional attempts on failure (1s, 5s, 30s).
Example: Node.js receiver
// Node.js / Express
const express = require('express');
const crypto = require('crypto');
const app = express();
app.use(express.json());
app.post('/citai-webhook', (req, res) => {
// Verify signature (optional, if secret configured)
const signature = req.headers['x-citai-signature'];
if (signature) {
const expected = 'sha256=' + crypto
.createHmac('sha256', process.env.WEBHOOK_SECRET)
.update(JSON.stringify(req.body))
.digest('hex');
if (signature !== expected) {
return res.status(401).send('Invalid signature');
}
}
const { event } = req.body;
switch (event) {
case 'escalation.triggered':
// Create ticket in your CRM
break;
case 'low_confidence':
// Alert your team
break;
case 'feedback.negative':
// Log for review
break;
}
res.status(200).send('OK');
});
app.listen(3000);4 Plans & Limits
Usage limits per plan. FAQ matches are always unlimited on all plans.
| Feature | Free | Starter | Pro |
|---|---|---|---|
| AI Queries / month | 50/mo | 500/mo | 5,000/mo |
| FAQ Matches | ∞ | ∞ | ∞ |
| Knowledge Bases | 1 | 5 | 20 |
| Documents per KB | 10 | 50 | 200 |
| API Keys | 1 | 3 | 10 |
| Storage | 100 MB | 500 MB | 2 GB |
| Team Members | 1 | 5 | 20 |
| Webhooks | — | ✓ | ✓ |
| Smart Routing | — | ✓ | ✓ |
FAQ matches do not consume AI queries. They are always free and unlimited.
5 Knowledge Bases & Documents
Create knowledge bases, upload documents and configure access control for your organization.
Create & upload documents
Each Knowledge Base (KB) is a document container with its own RAG configuration.
- Go to Knowledge Bases in the sidebar and click 'New KB'
- Upload documents by dragging files or pasting a URL (HTML, PDF, DOCX)
- Processing (chunking + embeddings) happens in the background with progress tracking
Access control
Each KB has a visibility level that determines who can query it.
All tenant members can query it.
Only the creator and admins can access it.
Only members of the assigned groups have access.
Document processing
When you upload a document, Citai processes it automatically in multiple stages.
- Chunking: splits the document into semantic fragments of configurable size
- Embeddings: generates 384-dimension vectors with a multilingual model (ES/EN/PT/DE/FR/IT)
- Contextual Retrieval: optionally enriches each chunk with full document context via LLM
| Format | Extension | Max size | Description |
|---|---|---|---|
| 50 MB | PDF documents with text and page extraction | ||
| DOCX | .docx | 50 MB | Word documents with preserved formatting |
| Excel | .xlsx / .xls | 50 MB | Excel spreadsheets (.xlsx and .xls). Use descriptive headers in row 1 for best results. All sheets are extracted as tabular text. |
| TXT | .txt | 10 MB | Plain text files |
| Markdown | .md | 10 MB | Markdown files with formatting |
| URL | N/A | N/A | URL ingestion: auto-detects HTML, PDF or DOCX |
6 RAG Configuration
Each Knowledge Base has its own RAG configuration. Adjust these parameters from the KB settings or the Playground to optimize results.
| Parameter | Type | Default | Description |
|---|---|---|---|
| temperature | float | 0.3 | LLM creativity. Lower values produce more deterministic answers. |
| top_k | int | 5 | Number of chunks to retrieve in hybrid search. |
| similarity_threshold | float | 0.3 | Minimum vector similarity threshold to include a chunk. |
| use_reranking | bool | true | Enable cross-encoder reranking to reorder by real relevance. |
| use_mmr | bool | true | Enable MMR (Maximal Marginal Relevance) for result diversity. |
| mmr_diversity | float | 0.3 | MMR diversity factor. Higher values reduce redundancy. |
| max_search_rounds | int | 3 | Maximum autonomous search rounds (Agentic RAG). Up to 3. |
| cache_enabled | bool | true | Enable semantic cache for repeated responses. |
| cache_ttl_hours | int | 168 | Cache time-to-live in hours (default: 7 days). |
| language_auto_detect | bool | true | Automatically detect query language (ES/EN/PT/DE/FR/IT). |
| language_override | string | null | null | Force a specific language for all responses. |
| faq_threshold | float | 0.75 | Confidence threshold for a valid FAQ match. |
These parameters can be adjusted per KB from Settings > RAG or from the Playground with real-time preview.
7 FAQ Management
FAQs provide instant answers without consuming AI queries. They are evaluated before the RAG pipeline.
Manual creation
Create FAQs one by one from the FAQ section of each Knowledge Base. Define question, answer and optionally multimedia attachments (images, videos, links).
Bulk import (CSV)
Import hundreds of FAQs from a CSV file with columns: question, answer, category. Ideal for migrations or existing knowledge bases.
Multimedia & attachments
Each FAQ can include images (with crop editor), videos and links. Image limits depend on the plan: Starter 3/FAQ, Pro 5/FAQ, Enterprise 10/FAQ.
Auto-suggested FAQs
Citai detects recurring unanswered queries and automatically generates suggested FAQs using AI. Admins can approve or reject them from the suggested FAQs panel.
Matching flow
When a user asks a question, the system searches for similar FAQs before querying the LLM.
FAQ matches are always free and do not consume the AI query budget on any plan.
8 API Keys & Rate Limiting
Manage access keys for the embeddable widget and REST API. Each key has configurable permissions and limits.
Creating API Keys
Generate keys with the sk- prefix from Dashboard > API Keys. Each key is associated with specific Knowledge Bases and can be configured with allowed CORS origins.
Security
API keys are shown only once upon creation. They are stored hashed with SHA-256 in the database. If you lose a key, you must regenerate it.
Rate Limiting
Each API key has a per-minute request limit implemented with Redis sliding window (sorted sets). If the limit is exceeded, HTTP 429 is returned. If Redis is unavailable, rate limiting fails open.
Dynamic CORS
Each API key can have configured allowed origins. The CORS middleware validates the origin of each request against the key's list. This ensures the widget only works on authorized domains.
| Plan | Max API Keys | Rate Limit |
|---|---|---|
| Free | 1 | 10 req/min |
| Starter | 3 | 30 req/min |
| Pro | 10 | 100 req/min |
9 Live Chat & Human Handoff
Allows human agents to take over widget conversations when AI is not enough.
How it works
When a widget visitor needs human help (due to low confidence or manual escalation), the session enters a waiting queue. A team agent can claim the session from the Live Chat panel and respond in real time via SSE.
Session states
Agent panel
Agents access the panel from the sidebar to manage live chat sessions.
- Waiting queue: sessions pending to be claimed
- Active sessions: ongoing conversations with the agent
- History: resolved sessions with complete records
When it triggers
Handoff triggers automatically when response confidence is below the configured threshold, or manually when the visitor clicks the escalation button. Concurrency is handled with SELECT FOR UPDATE to prevent two agents from claiming the same session.
10 Smart Routing
Automatic Knowledge Base selection based on the semantics of the user's query.
How it works
Smart Routing computes centroids (average embeddings) of each KB and compares the user's query against these centroids using cosine similarity. It selects the top-3 most relevant KBs.
Minimum threshold
Only KBs with cosine similarity >= 0.15 are included. If no KB exceeds the threshold, all available tenant KBs are used.
Centroid cache
Centroids are cached in Redis for 24 hours. They are automatically invalidated when new documents are uploaded, reprocessed or FAQs are created.
Usage in Chat & Widget
In chat, enable the 'Auto' toggle to use Smart Routing. It disables automatically when you manually select a KB. In the widget, auto-routing applies when the API key has access to multiple KBs.
11 Analytics & Export
Comprehensive dashboard with real-time metrics, interactive charts and data export.
Total queries
Resolution rate
Cost per query
Cache hit rate
FAQ savings
Tag distribution
Interactive charts
Visualize your assistant's performance with Chart.js charts.
- Time series: queries per day/week/month with range filters
- Resolution doughnut: resolved, escalated and negative
- Horizontal bars: tag distribution with per-conversation detail
Knowledge gap detection
The system identifies queries that didn't get good answers and groups them by frequency. You can mark them as resolved, filter by KB or generate suggested FAQs directly.
AI auto-tagging
Each conversation is automatically tagged with one of 15 canonical categories using a background LLM call (fire-and-forget). Tags are used for analytics and filtering.
Data export
Export all analytics metrics in CSV or JSON format from the export button on the dashboard. Includes queries, resolution, costs, tags and knowledge gaps.
Revenue vs Cost (super admin)
Super admin exclusive view showing revenue, costs, profit and margin per tenant. Includes global totals cards and a detailed per-organization table.
12 GDPR & Data Privacy
Citai complies with GDPR principles. Here are the tools and policies available to manage personal data.
Data export
Download a ZIP file with all your organization's data: conversations, FAQs, documents, configurations and usage logs. Available from Settings > Account.
Account deletion
Request account deletion with password confirmation. A soft delete is performed, marking data for permanent deletion according to the retention policy.
Retention policies
Retention policies are configurable by the administrator. Define how long conversations, audit logs and widget session data are kept.
Audit log
Records over 15 event types (login, creation, deletion, config changes). Configurable retention with date-based purge. Accessible from the admin panel.
Stored data
Citai stores the following data, all isolated per tenant:
- Conversations and messages (with confidence metadata and sources)
- Original documents and their processed chunks
- Vector embeddings (384 dimensions) associated with chunks and FAQs
- Usage logs (queries, tokens, provider, query type) and audit logs
How to request export or deletion
From the app: go to Settings > Account to export data or request deletion. By email: contact [email protected] with your registered email address.
13 Tools & Auto-Inject
Connect external APIs so the agent can access real-time data. Two modes: the LLM decides when to call (flexible) or Auto-Inject from widget filters (deterministic).
LLM Mode
The AI model analyzes the user's question and decides if it needs external data. Flexible, ideal for optional tools.
E.g.: weather, exchange rates, news — the LLM calls only when relevant.
Auto-Inject Mode
The tool runs automatically with widget intake filter data. 100% deterministic, no LLM dependency.
E.g.: CRM, ERP, ticketing — customer data available from the first message.
Auto-Inject Flow
The system maps widget filters directly to API parameters. The result is injected as context before the LLM generates the response, combined with the KB.
How to configure (4 steps)
- 1Set up an intake filter in your API Key (Settings > API Keys) with a key like 'customer_code'.
- 2Create a tool in Tools > New. Configure endpoint, auth, and parameters.
- 3Select 'Auto-inject from filters' and map each tool parameter to a widget filter.
- 4Test: open the widget, complete the intake, send a message. You'll see 'Loading your data...' before the response.
Widget SSE Events
| Event | Status | Description |
|---|---|---|
| tool_call | calling | The LLM decided to call a tool (LLM mode). |
| tool_call | auto_inject | Auto-Inject tool executing with intake data. |
| tool_call | success | Tool executed successfully, result available. |
| tool_call | error | Error executing the tool (timeout, HTTP error, etc). |
Test Template: CRM Demo
DemoUse the 'CRM Demo (test)' template when creating a tool. Connects to DummyJSON (free public API) to simulate a customer lookup. Set up an intake filter with key 'nombre_cliente' in your API key.
Per-widget tool filtering
Each API key can select which tools it uses. If tool_ids is null, it uses all tools. If it has IDs, only those tools. Configurable in Settings > API Keys > Enabled tools.
| Field | Value | Description |
|---|---|---|
| tool_ids | null | Uses all tenant tools (default, backward compatible). |
| tool_ids | [uuid, ...] | Uses only the tools with those IDs. |
Visual RAG: Document Images
Automatically extracts images from PDFs and DOCX and displays them in chat responses alongside relevant sources.
How it works
Images are extracted during document processing, filtered (min 50x50px, dedup by hash, max 50/doc), and associated with chunks by page proximity. At runtime, images are included in the SSE sources event with zero latency impact.
SSE format: sources with images
The 'sources' event now includes an 'images' array in each source that has associated images:
{
"sources": [{
"document_name": "manual.pdf",
"page": 21,
"score": 0.73,
"images": [
{ "url": "/api/v1/doc-images/.../img.jpg", "caption": null, "width": 800, "height": 600 }
]
}]
}RAG Configuration
Extraction is controlled via processing.extract_images (default: true). Can be toggled per-KB from the RAG Configuration dialog.
{
"processing": {
"extract_images": true
}
}Image Manager Endpoints
| Endpoint | Method | Description |
|---|---|---|
| /documents/{id}/images | GET | List document images with associations |
| /documents/{id}/images/upload | POST | Upload manual image to document |
| /documents/{id}/images/{img}/associate | PATCH | Associate image with a chunk |
| /documents/{id}/images/{img}/dissociate | PATCH | Dissociate image from a chunk |
| /documents/{id}/images/chunks | GET | List document chunks (for association UI) |
Semantic Cache
Vector-similarity response cache. If a new query has an embedding with cosine >= 0.95 against a cached entry from the same KB and same rag_config hash, the cached response is served without invoking the LLM.
When it runs
After FAQ matching and before the RAG pipeline. Cache hits emit 'cached: true' in the SSE done event and are counted as query_type='cached' in usage_logs. Automatic invalidations happen on document reprocessing, FAQ create/update, and new doc upload.
RAGConfig parameters
Configurable per KB inside the retrieval block. Default: cache enabled with a 7-day TTL (168 hours). Valid range: 1 to 720 hours.
{
"retrieval": {
"cache_enabled": true,
"cache_ttl_hours": 168
}
}Endpoints
| Endpoint | Method | Description |
|---|---|---|
| /admin/cache/stats | GET | Stats: total entries, total hits, cached_queries, hit_rate |
| /admin/cache/flush | POST | Flush per tenant or per KB (body: [kb_id?: UUID]) |
| /admin/cache/entries | GET | Paginated list with filters (search, origin, sort) |
| /admin/cache/entries/{'{'}entry_id{'}'} | GET | Entry detail (full text, sources, embedding) |
| /admin/cache/entries/{'{'}entry_id{'}'} | DELETE | Delete a single entry or bulk |
KB Health Score
0-100 score that measures the operational quality of each Knowledge Base, based on 5 weighted components. Cached in Redis for 1 hour; invalidated on doc upload/reprocess and FAQ create/update.
Components
Each component returns a 0-100 score with detailed breakdown (counts and averages). The final score is the weighted average.
Endpoints
| Endpoint | Method | Description |
|---|---|---|
| /knowledge-bases/health-summary | GET | Health score summary across all tenant KBs |
| /knowledge-bases/{'{'}kb_id{'}'}/health | GET | Per-component breakdown + prioritized recommendations |
Smart Routing
When an API key has multiple KBs or the user enables 'Auto' in chat, route_query() compares the query embedding against each KB centroid (AVG of chunk embeddings) using cosine similarity. Returns up to top 3 KBs with score >= 0.15. Centroids cached in Redis for 24h.
Auto-FAQ / Suggested FAQs
Automatically generates FAQ answers from the KB context for unanswered queries (knowledge gaps). Suggestions remain pending until an admin approves them (creates a real FAQ) or rejects them.
Flow
1) A query is logged as a knowledge gap. 2) Admin invokes suggest-faq (single or batch top 5). 3) The service skips queries with an existing similar FAQ (>= 0.85) or pending suggestion. 4) Calls the LLM to generate an answer from the KB context. 5) Stores in suggested_faqs with embedding. 6) Admin approves or rejects from the UI.
Endpoints
| Endpoint | Method | Description |
|---|---|---|
| /admin/analytics/unanswered/{'{'}id{'}'}/suggest-faq | POST | Generate suggestion for one unanswered query (body: [kb_id]) |
| /admin/analytics/unanswered/batch-suggest | POST | Batch generate top N unanswered for a KB (body: [kb_id, limit=5]) |
| /admin/suggested-faqs | GET | Paginated list (filters: status, kb_id) |
| /admin/suggested-faqs/{'{'}id{'}'}/approve | POST | Approve — creates the real FAQ and invalidates cache |
| /admin/suggested-faqs/{'{'}id{'}'}/reject | POST | Reject — marks the suggestion as rejected |
Content Rules
Per-KB declarative rules to block, redirect or filter sensitive content. Stored in the content_rules block of each KB's rag_config JSONB.
Rule types
- BLOCKBLOCK — cuts the flow and emits SSE content_blocked with the custom response. Evaluated after FAQ + cache, before RAG.
- REDIRECTREDIRECT — same as block but with type='redirect' (e.g. handoff to human support).
- FILTERFILTER — does not cut the flow. Applied to retrieved chunks before passing them to the LLM, removing those that contain the triggers.
Schema in rag_config
Each rule has type, list of triggers (keywords that fire it), optional response (max 500 chars, only for block/redirect) and enabled.
{
"content_rules": [
{
"type": "block",
"triggers": ["password", "credit card"],
"response": "I can't help with that.",
"enabled": true
},
{
"type": "filter",
"triggers": ["internal-only"],
"enabled": true
}
]
}SSE event content_blocked
Emitted when a query matches a BLOCK or REDIRECT rule. The widget and chat have native handlers to display the response without invoking the LLM.
event: content_blocked
data: {"type": "block", "response": "I can't help with that."}Audit Log
Per-tenant log of critical events: login, configuration changes, KB/FAQ/API key create and delete, etc. Filterable by user, action, resource type and date range.
Endpoints
| Endpoint | Method | Description |
|---|---|---|
| /admin/audit-log | GET | Paginated list with filters: user_id, action, resource_type, date_from, date_to, search |
| /admin/audit-log/export | GET | Export results as CSV |
| /admin/audit-log/retention | GET | Read retention config in days (super_admin) |
| /admin/audit-log/retention | PUT | Update retention days (super_admin) |
| /admin/audit-log/purge | POST | Purge logs older than retention (super_admin) |
Tracked actions
The backend emits these action keys via audit_service. You can filter by any of them.
Full REST API
For advanced integrations, check the OpenAPI reference with all available endpoints.
Open Swagger UI