Sync SDK
The @informedai/sync package syncs data from your backend to InformedAI's knowledge library. Your synced data is automatically chunked, embedded, and made available for RAG-powered AI responses across all InformedAI products (assistant widget, chatbots, questionnaires).
Installation#
npm install @informedai/syncSetup#
Before using this SDK, create a sync source in the InformedAI dashboard:
- Open the InformedAI dashboard
- Navigate to the playground canvas
- Click "+" and select "Sync Source"
- Enter a name for your sync source
- Copy the generated API key (
ssk_...)
Keep your ssk_ key on your server. Never expose it in client-side code.
Quick Start#
Pattern 1: Manual Push#
For backends using raw SQL queries, HTTP clients, or any data source:
import { InformedAI } from '@informedai/sync';
import { pool } from './db';
const ai = new InformedAI({
apiKey: process.env.INFORMEDAI_SYNC_KEY!, // ssk_... from dashboard
onDataRequest: async (req) => {
// Called when InformedAI needs data that's missing from cache
if (req.category === 'projects') {
const result = await pool.query(
'SELECT * FROM projects WHERE id = $1',
[req.externalId]
);
const project = result.rows[0];
return project
? { title: project.title, description: project.description }
: null;
}
return null;
},
});
// Start polling for recovery requests
ai.startPolling();
// Sync after creating/updating a project
app.post('/projects', async (req, res) => {
const project = await createProject(req.body);
// Fire-and-forget sync to InformedAI
ai.sync('projects', project.id, {
title: project.title,
description: project.description,
tech_stack: project.tech_stack,
}).catch(console.error);
res.json(project);
});
// Sync on delete
app.delete('/projects/:id', async (req, res) => {
await deleteProject(req.params.id);
ai.delete('projects', req.params.id).catch(console.error);
res.status(204).send();
});Pattern 2: Prisma Auto-Sync#
For backends using Prisma ORM, the SDK can automatically sync on create, update, and delete operations:
import { InformedAI } from '@informedai/sync';
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
const ai = new InformedAI({
apiKey: process.env.INFORMEDAI_SYNC_KEY!,
});
ai.prisma(prisma, {
models: {
BlogPost: {
category: 'blog_posts',
displayName: 'Blog Posts',
on: ['create', 'update'], // default
map: (post) => ({
title: post.title,
content: post.content,
excerpt: post.excerpt,
author: post.author,
}),
},
Event: {
category: 'events',
displayName: 'Golf Events',
on: ['create', 'update', 'delete'],
map: (event) => ({
name: event.name,
date: event.date.toISOString(),
description: event.description,
}),
},
},
});
ai.startPolling();The Prisma integration uses middleware to intercept operations. Syncs happen asynchronously (fire-and-forget) so they don't slow down your database operations.
Pattern 3: Batch Sync#
For syncing existing data in bulk (initial import or periodic full sync):
import { InformedAI } from '@informedai/sync';
const ai = new InformedAI({
apiKey: process.env.INFORMEDAI_SYNC_KEY!,
});
// Sync all existing projects
const projects = await pool.query('SELECT * FROM projects');
await ai.syncBatch(
'projects',
projects.rows.map((p) => ({
id: p.id,
data: {
title: p.title,
description: p.description,
tech_stack: p.tech_stack,
},
}))
);
console.log('Initial sync complete!');Constructor#
const ai = new InformedAI(config);| Option | Type | Required | Default | Description |
|--------|------|----------|---------|-------------|
| apiKey | string | Yes | — | Sync source API key (ssk_...) from dashboard |
| baseUrl | string | No | https://api.informedai.app | API base URL |
| onDataRequest | (req: DataRequest) => Promise<Record \| null> | No | — | Handler for recovery requests (see Recovery) |
| pollInterval | number | No | 30000 | Polling interval in milliseconds |
| debug | boolean | No | false | Enable debug logging to console |
Methods#
sync(category, id, data)#
Sync a single record to InformedAI.
await ai.sync('projects', 'proj_123', {
title: 'My Project',
description: 'A great project',
tech_stack: 'React, Node.js',
});| Parameter | Type | Description |
|-----------|------|-------------|
| category | string | Category name (e.g., "projects") |
| id | string | External ID of the record |
| data | Record<string, unknown> | Data fields to sync |
If the category hasn't been registered yet, it's auto-registered with columns inferred from the data keys.
syncBatch(category, records)#
Sync multiple records at once. More efficient than calling sync() in a loop.
await ai.syncBatch('projects', [
{ id: 'proj_1', data: { title: 'Project 1', description: '...' } },
{ id: 'proj_2', data: { title: 'Project 2', description: '...' } },
]);delete(category, id)#
Delete a record from InformedAI.
await ai.delete('projects', 'proj_123');rebuild(category)#
Force rebuild knowledge chunks for a category. Useful after bulk changes or if data gets out of sync.
await ai.rebuild('projects');registerCategory(config)#
Explicitly register a category with a display name and column list. Called automatically by sync() and syncBatch(), but you can call it manually to control the display name.
await ai.registerCategory({
name: 'projects',
displayName: 'Client Projects',
columns: ['title', 'description', 'tech_stack', 'status'],
});getStatus()#
Get the sync source status, including category stats.
const status = await ai.getStatus();
console.log(status.name); // "My Sync Source"
console.log(status.categories); // [{ name: 'projects', recordCount: 42 }]startPolling() / stopPolling()#
Start or stop polling for recovery requests. Call startPolling() when your server starts and stopPolling() on shutdown.
ai.startPolling();
// On shutdown
process.on('SIGTERM', () => {
ai.stopPolling();
});Even if you don't have an onDataRequest handler, polling sends heartbeats so the dashboard shows your sync source as online.
heartbeat()#
Manually send a heartbeat. Called automatically during polling, but you can call it directly if you're not using polling.
await ai.heartbeat();prisma(client, config)#
Set up automatic Prisma middleware for syncing.
ai.prisma(prisma, {
models: {
ModelName: {
category: 'category_name',
displayName: 'Display Name', // optional
on: ['create', 'update', 'delete'], // default: ['create', 'update']
map: (record) => ({ field: record.field }),
getId: (record) => record.customId, // optional, defaults to record.id
},
},
});| Config field | Type | Required | Description |
|-------------|------|----------|-------------|
| category | string | Yes | Sync category name |
| displayName | string | No | Human-readable name for the dashboard |
| on | string[] | No | Operations to sync on: 'create', 'update', 'delete' |
| map | (record) => object | Yes | Transform the Prisma record to the data you want synced |
| getId | (record) => string | No | Custom ID extractor (defaults to record.id) |
Recovery#
When the system needs data that's missing from its cache, it creates recovery requests. If you've configured an onDataRequest handler, the SDK automatically polls for these requests, calls your handler, and re-syncs the data.
const ai = new InformedAI({
apiKey: process.env.INFORMEDAI_SYNC_KEY!,
onDataRequest: async (req) => {
// req.category - which category the data belongs to
// req.externalId - the record ID that needs recovery
const record = await db.findById(req.category, req.externalId);
if (!record) return null; // Record no longer exists
return {
title: record.title,
description: record.description,
};
},
});
ai.startPolling(); // Polls every 30s by defaultThe DataRequest object passed to your handler:
| Field | Type | Description |
|-------|------|-------------|
| id | string | Request ID (used internally) |
| category | string | Category name |
| externalId | string | The record's external ID |
How It Works#
-
Data Storage — Your data is cached in Redis for fast retrieval and chunked into pgvector-backed knowledge for semantic search.
-
Knowledge Chunks — Each record is split into chunks, embedded using Voyage AI, and stored with vector embeddings. The AI assistant searches these chunks when generating content.
-
Recovery — If cached data is lost, InformedAI creates recovery requests. Your SDK polls for these and resends the data automatically.
-
Visualization — Sync sources appear as nodes in the InformedAI playground canvas, showing category counts and connection status.
Environment Variables#
INFORMEDAI_SYNC_KEY=ssk_your_sync_source_key_hereDebug Mode#
Enable debug logging to see all API calls:
const ai = new InformedAI({
apiKey: process.env.INFORMEDAI_SYNC_KEY!,
debug: true,
});Output:
[InformedAI:My Source] GET /source
[InformedAI:My Source] Source verified
[InformedAI:My Source] POST /categories
[InformedAI:My Source] Category registered: projects
[InformedAI:My Source] POST /categories/projects/data