Skip to content

Notifications, Analytics & Metrics

Copy page

The Server SDK exposes four observability clients for AI releases:

ClientWhat it covers
divinci.notificationsNotifications + custom flaggers, delivery channels, triggers
divinci.analyticsProduct analytics: metrics, trends, experiments, funnel, events
divinci.metricsPipeline telemetry + custom alert configurations
divinci.usageWorkspace API-key usage stats + billing reports
divinci.feedbackReading the message feedback your releases received

All methods take the workspaceId as their first argument.

import { DivinciServer } from "@divinci-ai/server";
const divinci = new DivinciServer({ apiKey: process.env.DIVINCI_API_KEY });
// List unread
const { docs } = await divinci.notifications.list("ws-123", { isRead: false });
// Counts, mark-read, tags, notes
const { unread } = await divinci.notifications.getCounts("ws-123");
await divinci.notifications.markAsRead("ws-123", notifId, true);
await divinci.notifications.setTags("ws-123", notifId, ["urgent"]);
await divinci.notifications.addNote("ws-123", notifId, "Escalated to ops");

The full custom-implementation flow — flagger detects, tags the notification, a trigger matches the tags, a channel delivers it to your infrastructure:

// 1. Webhook channel — where notifications land
const channel = await divinci.notifications.channels.create("ws-123", {
channelName: "Ops webhook",
deliveryMethod: "webhook",
details: {
url: "https://ops.example.com/divinci-hook",
method: "POST",
headers: {},
secret: "min-32-character-hmac-secret-here!!",
retryPolicy: { maxRetries: 3, backoffType: "exponential", initialDelayMs: 1000, maxDelayMs: 60000 },
timeout: 10000,
},
});
await divinci.notifications.channels.test("ws-123", channel._id);
// 2. Trigger — route "urgent" notifications to that channel
await divinci.notifications.triggers.create("ws-123", {
title: "Escalations to ops",
description: "Urgent tags go to the ops webhook",
triggerByDefault: false,
rules: [{ mode: "if-any-tag", tags: ["urgent"], action: "triggered" }],
channels: [channel._id],
});
// 3. Flagger — the custom LLM detection that raises tagged notifications
const flagger = await divinci.notifications.flaggers.create("ws-123", {
title: "Refund-request detector",
notificationIcon: "💸",
notificationMessage: "A user asked about refunds",
notificationTags: ["refund", "urgent"],
assistant: "gemini-3-5-flash",
prefix: "Does this message ask for a refund? Reply with JSON {\"flagged\": boolean}",
outputKeys: ["flagged"],
flagInput: true,
notifyUser: false,
});
// Dry-run before going live (billed like a normal LLM call)
await divinci.notifications.flaggers.testRun("ws-123", flagger._id, "I want my money back!");

Email channels need verification: channels.sendVerificationEmail(wsId, channelId) sends a clickable link; channels.deliveryLogs(wsId, channelId) shows the last 100 webhook deliveries.

// Aggregated metrics with previous-period comparison
const { current, previous } = await divinci.analytics.metrics("ws-123", { timeRange: "7d" });
// Trends / per-product performance / experiments / funnel
const { trends } = await divinci.analytics.trends("ws-123", { granularity: "daily" });
const { products } = await divinci.analytics.performance("ws-123", { sortBy: "ctr" });
const { experiments } = await divinci.analytics.experiments("ws-123", { status: "running" });
const { funnel } = await divinci.analytics.funnel("ws-123");
// Record interaction events (max 50/batch)
await divinci.analytics.trackEvents("ws-123", [
{ event: "product_click", productId: "sku-1", sessionId: "s-1" },
]);
// Per-node pipeline telemetry
const result = await divinci.metrics.pipeline("ws-123", "pipe-1", { timeRange: "24h" });
// Custom alert config → webhook + email
await divinci.metrics.setAlertConfig("ws-123", "pipe-1", {
nodeType: "embedding",
metric: "errorRate",
thresholds: { warning: 0.05, critical: 0.2 },
notificationChannels: [
{ type: "webhook", config: { url: "https://ops.example.com/alert" } },
],
});
// Active alerts + acknowledge
const { alerts } = await divinci.metrics.alerts("ws-123", "pipe-1");
if (alerts[0]) await divinci.metrics.acknowledgeAlert("ws-123", "pipe-1", alerts[0].id);
// Export
const csv = await divinci.metrics.exportMetrics("ws-123", "pipe-1", { format: "csv" });
// Usage stats for a BYOK provider key (epoch-ms date range)
const { stats } = await divinci.usage.apiKeyUsage("ws-123", "openai", {
startDate: Date.now() - 30 * 24 * 3600 * 1000,
});
console.log(stats.totalCost, stats.successRate);
// BYOK vs Divinci-wallet billing comparison
const report = await divinci.usage.billingReport("ws-123");

divinci.feedback is the read side of message feedback — the ratings and free-text your releases received. (Submission is a consumer-facing surface, not part of the Server SDK.)

// Most recent negative feedback that carries written detail
const { docs } = await divinci.feedback.list("ws-123", {
sentiment: "negative", // "positive" | "negative" | "neutral"
source: "anonymous", // "authed" | "anonymous"
hasText: true,
page: 0,
limit: 25,
});
// A single record
const record = await divinci.feedback.get("ws-123", docs[0].id);
// Aggregate breakdown (server-computed)
const stats = await divinci.feedback.stats("ws-123", { releaseId: "rel-1" });
// → { total, bySentiment: { positive, negative, neutral },
// bySource: { authed, anonymous }, withText, byRelease: [...] }

sentiment buckets map to the stored value: positive = thumbs-up (1), negative = thumbs-down (-1), neutral = free-text with no vote (null).

divinci.releases.getAnalytics() is deprecated — there is no per-release analytics route in the v1 API and the method throws. Use divinci.analytics (workspace-scoped product analytics) or divinci.metrics (pipeline telemetry) instead.