@syncagent/js
v0.5.1
Published
SyncAgent JavaScript SDK — AI database agent for any app
Maintainers
Readme
@syncagent/js
Core JavaScript/TypeScript SDK for SyncAgent — add an AI database agent to any app.
Works with MongoDB, PostgreSQL, MySQL, SQLite, SQL Server, and Supabase.
Get Your API Key
- Sign up for a free account
- Go to your Dashboard → New Project → choose your database type
- Copy your API key (starts with
sa_)
Every new project gets a 14-day trial with 500 free requests — no credit card required. After the trial, you get 100 free requests/month on the Free plan.
Install
npm install @syncagent/jsQuick Start
import { SyncAgentClient } from "@syncagent/js";
const agent = new SyncAgentClient({
apiKey: "sa_your_api_key",
connectionString: process.env.DATABASE_URL,
});
// Non-streaming
const result = await agent.chat([
{ role: "user", content: "How many users signed up this month?" }
]);
console.log(result.text);
// Streaming
await agent.chat(
[{ role: "user", content: "Show me the top 10 customers by revenue" }],
{
onToken: (token) => process.stdout.write(token),
onComplete: (text) => console.log("\nDone"),
onError: (err) => console.error(err),
}
);Supported Databases
| Database | Connection String Format |
|----------|------------------------|
| MongoDB | mongodb+srv://user:[email protected]/mydb |
| PostgreSQL | postgresql://user:pass@host:5432/mydb |
| MySQL | mysql://user:pass@host:3306/mydb |
| SQLite | /absolute/path/to/database.sqlite |
| SQL Server | Server=host,1433;Database=mydb;User Id=user;Password=pass;Encrypt=true; |
| Supabase | https://xxx.supabase.co\|your-anon-key |
Configuration
new SyncAgentClient(config: SyncAgentConfig)| Option | Type | Required | Description |
|--------|------|----------|-------------|
| apiKey | string | ✅ | Your SyncAgent API key (sa_...) |
| connectionString | string | ✅* | Your database URL — sent at runtime, never stored. *Optional when toolsOnly: true. |
| tools | Record<string, ToolDefinition> | — | Custom tools the agent can call client-side |
| baseUrl | string | — | Override API URL (dev only) |
| filter | Record<string, any> | — | Mandatory query filter for multi-tenancy |
| operations | ("read"\|"create"\|"update"\|"delete")[] | — | Restrict operations for this session |
| toolsOnly | boolean | — | Disables all DB tools — agent only uses your custom tools |
| autoDetectPage | boolean | true | Auto-detect current page from window.location |
| systemInstruction | string | — | Custom agent instructions — personality, tone, rules |
| confirmWrites | boolean | false | Ask for confirmation before create/update/delete |
| language | string | — | Language the agent responds in (e.g. "French") |
| maxResults | number | 50 | Default max records per query |
| sensitiveFields | string[] | ["password","token","secret"] | Fields masked in responses |
| onBeforeToolCall | (name, args) => boolean | — | Called before each client tool. Return false to block. |
| onAfterToolCall | (name, args, result) => void | — | Called after each client tool executes |
client.chat(messages, options?)
const result = await agent.chat(messages, options);messages — Message[]
{ role: "user" | "assistant"; content: string }options — ChatOptions
| Option | Type | Description |
|--------|------|-------------|
| onToken | (token: string) => void | Called for each streamed text chunk |
| onComplete | (text: string) => void | Called with the full response text |
| onError | (error: Error) => void | Called on error |
| onStatus | (step: string, label: string) => void | Live status updates |
| onData | (data: ToolData) => void | Called when a DB tool returns structured data |
| onToolCall | (name: string, args: any, result: any) => void | Called when a custom tool executes |
| signal | AbortSignal | Cancel the request |
| context | Record<string, any> | Extra context injected into every message |
Returns Promise<ChatResult> → { text: string }
Status steps
onStatus fires with these step values:
"connecting"— connecting to the database"schema"— discovering schema"thinking"— AI is reasoning"querying"— executing a DB tool"done"— complete
client.getSchema()
const schema = await agent.getSchema();
// CollectionSchema[]Multi-turn Conversations
const history: Message[] = [];
history.push({ role: "user", content: "Show top 5 customers" });
const r1 = await agent.chat(history);
history.push({ role: "assistant", content: r1.text });
history.push({ role: "user", content: "Now show their total orders" });
const r2 = await agent.chat(history);Context & Auto Page Detection
The SDK automatically detects the current page from window.location on every message — zero config needed.
URL: /dashboard/orders/ord_123?tab=details
Auto-detected context:
currentPage: "orders"
currentPath: "/dashboard/orders/ord_123"
currentRecordId: "ord_123"
param_tab: "details"Pass additional context:
await agent.chat(messages, {
context: { userId: "user_123", userRole: "admin", orgName: "Acme Corp" }
});onData — React to Query Results
await agent.chat(messages, {
onData: (data) => {
console.log(data.collection); // "orders"
console.log(data.data); // array of result rows
console.log(data.count); // number of results
}
});Custom Tools
Give the agent capabilities beyond your database. Tools run entirely in your app — SyncAgent only sees the schema and result.
const agent = new SyncAgentClient({
apiKey: "sa_your_key",
connectionString: process.env.DATABASE_URL,
tools: {
sendEmail: {
description: "Send an email to a user",
inputSchema: {
to: { type: "string", description: "Recipient email" },
subject: { type: "string", description: "Subject line" },
body: { type: "string", description: "Email body" },
},
execute: async ({ to, subject, body }) => {
await mailer.send({ to, subject, text: body });
return { sent: true };
},
},
},
});Multi-tenant SaaS
Pass filter to scope every agent operation to the current user's organization. Enforced server-side.
const agent = new SyncAgentClient({
apiKey: "sa_your_key",
connectionString: process.env.DATABASE_URL,
filter: { organizationId: currentUser.orgId },
operations: currentUser.isAdmin
? ["read", "create", "update", "delete"]
: ["read"],
});Tools-only Mode
Build an AI assistant powered by your own APIs — no database access needed.
const agent = new SyncAgentClient({
apiKey: "sa_your_key",
toolsOnly: true,
tools: {
searchProducts: {
description: "Search products by name or category",
inputSchema: { query: { type: "string", description: "Search query" } },
execute: async ({ query }) => {
const res = await fetch(`/api/products?q=${query}`);
return res.json();
},
},
},
});Customer Chat
Route messages through the customer support pipeline — with persona, knowledge base, conversation flows, escalation, and AI fallback — instead of the direct database agent.
The customer chat API surface includes:
client.customerChat()— send messages through the support pipelineclient.rateConversation()— submit satisfaction ratings- Guest identification —
getGuestIdentity(),setGuestIdentity(),validateGuestForm(),generateGuestIdentifier() - Theme engine —
computeTheme()for WCAG-compliant color generation - Types —
CustomerChatResult,CustomerChatOptions,GuestIdentity,GuestFormConfig,FieldValidationResult
Initialization
import { SyncAgentClient } from "@syncagent/js";
const client = new SyncAgentClient({
apiKey: "sa_your_api_key",
externalUserId: "customer_123",
});Note:
customerModeis automatically enabled whenexternalUserIdis provided. You do not need to setcustomerMode: trueexplicitly.
Full Lifecycle Example
import { SyncAgentClient } from "@syncagent/js";
// 1. Initialize client in customer mode
const client = new SyncAgentClient({
apiKey: "sa_your_api_key",
externalUserId: "customer_123",
});
// 2. Send a message
const result = await client.customerChat("How do I reset my password?");
console.log(result.response);
// "To reset your password, go to Settings > Security > Reset Password..."
// 3. Continue the conversation
const followUp = await client.customerChat("That didn't work", {
conversationId: result.conversationId,
onEscalated: () => {
console.log("Conversation escalated to a human agent");
// Connect to Pusher for real-time agent messages
},
onResolved: (conversationId) => {
console.log(`Conversation ${conversationId} resolved`);
},
});
// 4. Rate the conversation after resolution
if (followUp.resolved) {
await client.rateConversation(followUp.conversationId, 5);
}Guest Mode (No External User ID)
For anonymous visitors, initialize with customerMode: true and no externalUserId. The SDK will require guest identification before chatting:
import { SyncAgentClient, generateGuestIdentifier, validateGuestForm } from "@syncagent/js";
const client = new SyncAgentClient({
apiKey: "sa_your_api_key",
customerMode: true,
guestForm: {
title: "Welcome!",
subtitle: "Tell us who you are to get started",
submitButtonText: "Start Chat",
},
});
// Validate form data before submitting
const validation = validateGuestForm({
name: "Jane Doe",
email: "[email protected]",
});
if (validation.valid) {
// Set guest identity — persists to localStorage
client.setGuestIdentity({
name: "Jane Doe",
email: "[email protected]",
phone: null,
guestId: generateGuestIdentifier("[email protected]"),
});
// Now customerChat() will work
const result = await client.customerChat("I need help with my order");
console.log(result.response);
}Configuration
| Option | Type | Required | Description |
|--------|------|----------|-------------|
| customerMode | boolean | — | Enable customer agent mode. Routes messages through the customer support pipeline. |
| externalUserId | string | — | Customer identifier for multi-tenant scoping. When provided, customerMode is automatically enabled and both chat() and customerChat() become available on the same instance. |
client.customerChat(message, options?)
Send a message through the customer support pipeline.
Error: Throws "SyncAgent: customerChat() requires externalUserId in config (or customerMode: true)" if called without externalUserId in the client config.
const result = await client.customerChat("How do I reset my password?", {
conversationId: "conv_abc123",
metadata: { page: "/settings" },
onEscalated: () => console.log("Escalated to human agent"),
onResolved: (id) => console.log(`Conversation ${id} resolved`),
});Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| message | string | ✅ | The customer's message |
| options | CustomerChatOptions | — | Optional configuration (see below) |
Returns Promise<CustomerChatResult>
CustomerChatOptions
| Field | Type | Description |
|-------|------|-------------|
| conversationId | string | Existing conversation ID to continue |
| metadata | Record<string, any> | Additional metadata to store on the conversation |
| onEscalated | () => void | Called when the conversation is escalated to a human agent |
| onResolved | (conversationId: string) => void | Called when the conversation is resolved |
| onGuestIdentified | (identity: GuestIdentity) => void | Called when a guest completes identification (JS-only usage) |
CustomerChatResult
| Field | Type | Description |
|-------|------|-------------|
| conversationId | string | The conversation ID (new or existing) |
| response | string | The agent's reply text |
| escalated | boolean | Whether the conversation was escalated to a human agent |
| autoReply | boolean | Whether this was an automatic reply (e.g. welcome message) |
| flowActive | boolean | Whether a conversation flow is currently active |
| resolved | boolean | Whether the conversation has been resolved |
| welcomeMessage | string \| undefined | Welcome message if this is a new conversation |
| sources | any[] \| undefined | Knowledge base sources used to generate the response |
| flowSession | { flowId: string; currentNodeId: string } \| undefined | Active flow session state |
client.rateConversation(conversationId, rating)
Submit a satisfaction rating for a completed conversation.
Error: Throws "SyncAgent: rateConversation() requires externalUserId in config" if called without externalUserId in the client config.
await client.rateConversation("conv_abc123", 5);Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| conversationId | string | ✅ | The conversation ID to rate |
| rating | number | ✅ | Integer between 1 and 5 |
Validation rules:
conversationIdmust be a non-empty stringratingmust be an integer between 1 and 5 (inclusive)- Throws an error if validation fails
Returns Promise<void>
Conversation Flows
When a customer's message matches a configured flow's trigger phrases, the agent enters a conversation flow — a scripted branching path that guides the customer through a series of decision points, responses, and terminal actions (resolve or escalate).
You can detect an active flow by checking the flowActive and flowSession fields in the CustomerChatResult response:
| Field | Type | Description |
|-------|------|-------------|
| flowActive | boolean | true when the conversation is currently inside a flow |
| flowSession | { flowId: string; currentNodeId: string } \| undefined | Identifies which flow and node the conversation is on |
Detecting and Handling Active Flows
const result = await client.customerChat("I want to check my order status");
if (result.flowActive && result.flowSession) {
console.log(`Flow active: ${result.flowSession.flowId}`);
console.log(`Current node: ${result.flowSession.currentNodeId}`);
// The response contains the current node's content (e.g. decision options)
console.log(result.response);
// Example: "How would you like to check your order?\n1. By order number\n2. By email address"
// Present the options to the user in your UI, then send their choice
// as the next message to continue the flow:
const followUp = await client.customerChat("By order number", {
conversationId: result.conversationId,
});
// Check if the flow is still active or has reached a terminal node
if (!followUp.flowActive) {
console.log("Flow completed");
}
}When flowActive is false, the conversation is handled by the standard AI agent (persona, knowledge base, or escalation). Flows take priority over the AI agent when a trigger phrase matches.
SyncAgentClient.createDual(config)
⚠️ Deprecated:
createDual()will be removed in a future major version. Use the unifiedSyncAgentClientconstructor instead — passexternalUserIddirectly to enable both modes on a single instance.
Create both a database agent and a customer agent from a single config. Useful for apps that serve both internal users (admins) and end-customers from the same codebase.
Legacy pattern (deprecated):
import { SyncAgentClient } from "@syncagent/js";
const { db, support } = SyncAgentClient.createDual({
apiKey: "sa_your_api_key",
connectionString: "postgresql://user:pass@host:5432/mydb",
externalUserId: currentUser.id,
});
// Admin: direct database queries
const result = await db.chat([{ role: "user", content: "Show all overdue invoices" }]);
// Customer: support pipeline (persona, flows, KB, escalation)
const reply = await support.customerChat("I need help with my order");
await support.rateConversation(reply.conversationId, 5);Migration
Before — using createDual():
import { SyncAgentClient } from "@syncagent/js";
const { db, support } = SyncAgentClient.createDual({
apiKey: "sa_your_api_key",
connectionString: "postgresql://user:pass@host:5432/mydb",
externalUserId: "customer_123",
});
const dbResult = await db.chat([{ role: "user", content: "Show all overdue invoices" }]);
const supportResult = await support.customerChat("I need help with my order");After — unified constructor:
import { SyncAgentClient } from "@syncagent/js";
const client = new SyncAgentClient({
apiKey: "sa_your_api_key",
connectionString: "postgresql://user:pass@host:5432/mydb",
externalUserId: "customer_123",
});
const dbResult = await client.chat([{ role: "user", content: "Show all overdue invoices" }]);
const supportResult = await client.customerChat("I need help with my order");Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| config | DualClientConfig | ✅ | Shared configuration for both clients |
DualClientConfig
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| apiKey | string | ✅ | Your SyncAgent API key |
| connectionString | string | ✅ | Database connection string |
| externalUserId | string | ✅ | Customer identifier — scopes customer agent conversations |
| baseUrl | string | — | Override API base URL |
| filter | Record<string, any> | — | Mandatory query filter for multi-tenant scoping |
| operations | string[] | — | Restrict allowed operations (read, create, update, delete) |
| tools | Record<string, ToolDefinition> | — | Custom tools for the database agent |
| systemInstruction | string | — | Custom system prompt |
| language | string | — | Response language |
Returns { db: SyncAgentClient, support: SyncAgentClient }
db— Database agent instance (customerMode: false). Usedb.chat()for direct DB queries.support— Customer agent instance (customerMode: true). Usesupport.customerChat()for the support pipeline.
Guest Identification
When using Customer Agent Mode without an externalUserId, the SDK supports a guest identification flow. Guests provide their name and email before chatting, and their identity is persisted to localStorage for returning visits.
Client Methods
client.getGuestIdentity()
Returns the stored guest identity, or null if no identity has been saved.
const identity = client.getGuestIdentity();
// GuestIdentity | nullclient.setGuestIdentity(identity)
Persists a guest identity to localStorage and sets the externalUserId for subsequent API calls.
import { generateGuestIdentifier } from "@syncagent/js";
client.setGuestIdentity({
name: "Jane Doe",
email: "[email protected]",
phone: null,
guestId: generateGuestIdentifier("[email protected]"),
});client.getGuestFormConfig()
Returns the GuestFormConfig passed at construction (via the guestForm option), or undefined if none was provided.
const formConfig = client.getGuestFormConfig();
// GuestFormConfig | undefinedonGuestIdentified Callback
Use the onGuestIdentified callback in CustomerChatOptions to react when a guest completes identification:
import { SyncAgentClient } from "@syncagent/js";
const client = new SyncAgentClient({
apiKey: "sa_your_api_key",
customerMode: true,
guestForm: {
title: "Hi there!",
subtitle: "Tell us who you are to get started",
submitButtonText: "Begin Chat",
},
});
const result = await client.customerChat("Hello", {
onGuestIdentified: (identity) => {
console.log(`Guest identified: ${identity.name} (${identity.guestId})`);
},
});Utility Functions
| Function | Signature | Description |
|----------|-----------|-------------|
| validateName | (name: string) => FieldValidationResult | Validates that name contains at least one non-whitespace character |
| validateEmail | (email: string) => FieldValidationResult | Validates email matches [email protected] pattern |
| validateGuestForm | (data: { name, email, phone? }) => { valid: boolean; errors: Record<string, string> } | Validates the complete guest form submission |
| generateGuestIdentifier | (email: string) => string | Generates a deterministic guest_-prefixed ID from the email (FNV-1a hash) |
import {
validateName,
validateEmail,
validateGuestForm,
generateGuestIdentifier,
} from "@syncagent/js";
// Individual field validation
const nameResult = validateName("Jane");
// { valid: true }
const emailResult = validateEmail("not-an-email");
// { valid: false, error: "Please enter a valid email address" }
// Full form validation
const formResult = validateGuestForm({
name: "Jane Doe",
email: "[email protected]",
phone: "+1234567890",
});
// { valid: true, errors: {} }
// Generate a deterministic guest ID
const guestId = generateGuestIdentifier("[email protected]");
// "guest_a1b2c3d4"Types
import type {
GuestIdentity,
GuestFormConfig,
FieldValidationResult,
} from "@syncagent/js";GuestIdentity
| Field | Type | Description |
|-------|------|-------------|
| name | string | Guest's display name |
| email | string | Guest's email address |
| phone | string \| null | Optional phone number |
| guestId | string | Deterministic identifier (guest_ + FNV-1a hex hash of email) |
GuestFormConfig
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| title | string | "Welcome" | Form heading text |
| subtitle | string | "Please introduce yourself to get started" | Form description text |
| submitButtonText | string | "Start Chat" | Submit button label |
| namePlaceholder | string | — | Placeholder for the name input |
| emailPlaceholder | string | — | Placeholder for the email input |
| phonePlaceholder | string | — | Placeholder for the phone input |
| className | string | — | Custom CSS class for the form container |
| onSubmit | (identity: GuestIdentity) => void | — | Called after successful form submission |
FieldValidationResult
| Field | Type | Description |
|-------|------|-------------|
| valid | boolean | Whether the field passed validation |
| error | string \| undefined | Error message if validation failed |
Theme Engine
The computeTheme() function generates a complete, WCAG-compliant color palette from an accent color and dark mode flag. Used internally by the pre-built <SyncAgentCustomerChat> components across all framework packages (React, Angular, Vue).
computeTheme(accentColor?, darkMode?)
import { computeTheme } from "@syncagent/js";
const theme = computeTheme("#6366f1", false);
// ThemeColors object with all color tokensParameters
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| accentColor | string | "#6366f1" | CSS color string (hex #RGB/#RRGGBB, rgb(), or hsl()). Falls back to default if invalid. |
| darkMode | boolean | false | Whether to generate a dark color scheme |
Returns ThemeColors
ThemeColors
| Field | Type | Description |
|-------|------|-------------|
| background | string | Main background color |
| surface | string | Elevated surface color (cards, panels) |
| text | string | Primary text color (4.5:1 contrast against background) |
| textSecondary | string | Secondary text color (4.5:1 contrast against background) |
| border | string | Border color for dividers and outlines |
| accent | string | Primary accent color (3:1 contrast against background) |
| accentHover | string | Accent color on hover state |
| userBubble | string | User message bubble background |
| userBubbleText | string | User message bubble text (4.5:1 contrast against userBubble) |
| assistantBubble | string | Assistant message bubble background |
| assistantBubbleText | string | Assistant message bubble text (4.5:1 contrast against assistantBubble) |
| inputBackground | string | Text input background color |
| inputBorder | string | Text input border color |
WCAG Contrast Guarantees
computeTheme() ensures the following minimum contrast ratios for any valid input:
- 4.5:1 —
textagainstbackground - 4.5:1 —
textSecondaryagainstbackground - 4.5:1 —
userBubbleTextagainstuserBubble - 4.5:1 —
assistantBubbleTextagainstassistantBubble - 3:1 —
accentagainstbackground
Contrast Utilities
import { relativeLuminance, contrastRatio } from "@syncagent/js";
// Compute relative luminance (WCAG 2.1) for an RGB color
const lum = relativeLuminance(99, 102, 241); // 0-1
// Compute contrast ratio between two RGB colors
const ratio = contrastRatio([255, 255, 255], [24, 24, 27]); // 1-21| Function | Signature | Description |
|----------|-----------|-------------|
| relativeLuminance | (r: number, g: number, b: number) => number | WCAG 2.1 relative luminance (0–1) for an RGB color (0–255 per channel) |
| contrastRatio | (color1: [r,g,b], color2: [r,g,b]) => number | Contrast ratio (1–21) between two RGB colors |
Example: Custom Theme Integration
import { computeTheme, type ThemeColors } from "@syncagent/js";
// Light mode with custom brand color
const lightTheme: ThemeColors = computeTheme("#e11d48", false);
// Dark mode
const darkTheme: ThemeColors = computeTheme("#e11d48", true);
// Apply to your UI
document.documentElement.style.setProperty("--chat-bg", lightTheme.background);
document.documentElement.style.setProperty("--chat-text", lightTheme.text);
document.documentElement.style.setProperty("--chat-accent", lightTheme.accent);Unified Dual Mode
By passing externalUserId to the SyncAgentClient constructor alongside your connectionString, both chat() and customerChat() become available on the same instance — no need to call createDual() or manage separate clients.
This is the simplest way to build apps that serve both internal (admin/database) and customer-facing use cases from a single client.
import { SyncAgentClient } from "@syncagent/js";
const client = new SyncAgentClient({
apiKey: "sa_your_api_key",
connectionString: process.env.DATABASE_URL,
externalUserId: "customer_123",
});
// Database agent — direct queries
const dbResult = await client.chat([
{ role: "user", content: "Show all overdue invoices" }
]);
console.log(dbResult.text);
// Customer agent — support pipeline (persona, flows, KB, escalation)
const supportResult = await client.customerChat("I need help with my order", {
onEscalated: () => console.log("Escalated to human agent"),
onResolved: (id) => console.log(`Conversation ${id} resolved`),
});
console.log(supportResult.response);Abort / Cancel
const controller = new AbortController();
agent.chat(messages, { signal: controller.signal });
controller.abort();Express.js Integration
import express from "express";
import { SyncAgentClient } from "@syncagent/js";
const app = express();
app.use(express.json());
const agent = new SyncAgentClient({
apiKey: process.env.SYNCAGENT_KEY,
connectionString: process.env.DATABASE_URL,
});
app.post("/chat", async (req, res) => {
res.setHeader("Content-Type", "text/plain");
res.setHeader("Transfer-Encoding", "chunked");
await agent.chat(req.body.messages, {
onToken: (token) => res.write(token),
onComplete: () => res.end(),
onError: (err) => { res.status(500).end(err.message); },
});
});
app.listen(3000);TypeScript Types
import type {
SyncAgentConfig, Message, ChatOptions, ChatResult,
CollectionSchema, SchemaField, ToolDefinition, ToolParameter, ToolData,
CustomerChatResult, CustomerChatOptions, DualClientConfig, DualClient,
DualChatReturn, DualChatOptions, UnifiedConfig,
GuestIdentity, GuestFormConfig, FieldValidationResult,
} from "@syncagent/js";
import type { ThemeColors } from "@syncagent/js";Customer Chat Types
| Type | Description |
|------|-------------|
| CustomerChatResult | Response from customerChat() — contains conversationId, response, escalated, resolved, flowActive, welcomeMessage, sources, flowSession |
| CustomerChatOptions | Options for customerChat() — includes conversationId, metadata, onEscalated, onResolved, onGuestIdentified |
| GuestIdentity | Guest user data — name, email, phone, guestId |
| GuestFormConfig | Guest form customization — title, subtitle, submitButtonText, placeholders, className, onSubmit |
| FieldValidationResult | Validation result — valid boolean and optional error message |
| ThemeColors | Complete color palette from computeTheme() — 13 color tokens for backgrounds, text, accents, and bubbles |
New Types Reference
| Type | Description | Import |
|------|-------------|--------|
| DualChatReturn | Return type for the useDualChat() hook and framework equivalents — contains db and support namespaces with messages, state, and actions | import type { DualChatReturn } from "@syncagent/js" |
| DualChatOptions | Options for the useDualChat() hook/composable — includes context, onData, onEscalated, and onResolved callbacks | import type { DualChatOptions } from "@syncagent/js" |
| UnifiedConfig | Config type alias for the unified client — SyncAgentConfig with externalUserId required | import type { UnifiedConfig } from "@syncagent/js" |
Security
- Your database connection string is never stored on SyncAgent servers
- It's passed at runtime, used to process the request, and immediately discarded
- API keys are hashed with bcrypt — raw keys are never stored
- Rate limiting: max 5 concurrent requests per API key per 10 seconds
Plans & Pricing
| Plan | Requests/mo | Collections | Price | |------|-------------|-------------|-------| | Free (+ 14-day trial) | 100 (500 during trial) | 5 | GH₵0 | | Starter | 5,000 | 20 | GH₵150/mo | | Pro | 50,000 | Unlimited | GH₵500/mo | | Enterprise | Unlimited | Unlimited | Custom |
Resources
License
MIT
