SDK API Reference v0.5.0
Complete public API of the @dicolytics/discord.js package.
createDicolytics
Factory function that constructs a Dicolytics instance and attaches it to the discord.js client. This is the recommended way to initialize the SDK.
function createDicolytics(client: Client, options: DicolyticsOptions): Dicolyticsdef create_dicolytics(bot: discord.Bot, **options) -> DicolyticsEquivalent to new Dicolytics(client, options).attach().
| Parameter | Type | Description |
|---|---|---|
client | Client | A discord.js v14 Client instance |
options | DicolyticsOptions | SDK configuration. See Configuration. |
Returns: A Dicolytics instance with all listeners attached and timers running.
Throws: Synchronously if apiKey is missing or empty, or if a Dicolytics instance is already attached to the same client.
Example
import { Client, GatewayIntentBits } from 'discord.js';
import { createDicolytics } from '@dicolytics/discord.js';
const client = new Client({
intents: [GatewayIntentBits.Guilds],
});
const analytics = createDicolytics(client, {
apiKey: process.env.DICOLYTICS_KEY!,
});
client.login(process.env.DISCORD_TOKEN);import os
import discord
from dicolytics import create_dicolytics
bot = discord.Bot()
analytics = create_dicolytics(bot, api_key=os.environ["DICOLYTICS_KEY"])
bot.run(os.environ["DISCORD_TOKEN"])Note
The SDK throws an error if you call createDicolytics on a client that already has an active instance. Call shutdown() first to detach, then re-attach.
await analytics.shutdown();
const newAnalytics = createDicolytics(client, { apiKey: '...' });track
Records a custom event. The event is queued and delivered asynchronously.
analytics.track(name: string, props?: Record<string, unknown>): voidanalytics.track(name: str, props: dict[str, Any] | None = None) -> None| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Event name (1--256 characters). Trimmed and truncated if necessary. |
props | Record<string, unknown> / dict | No | Arbitrary properties (max 50 keys, 8 KiB total serialized, 4 KiB per string value). |
Returns: void
Never throws. If the name is empty or not a string, the event is dropped with a debug log. SDK errors are silently ignored.
Examples
// Simple event
analytics.track('daily_reward_claimed');
// Event with properties
analytics.track('purchase', {
sku: 'pro',
amount: 42,
currency: 'USD',
});
// Event with context
analytics.track('ticket_opened', {
category: 'billing',
priority: 'high',
guildId: interaction.guildId,
});# Simple event
analytics.track("daily_reward_claimed")
# Event with properties
analytics.track("purchase", {
"sku": "pro",
"amount": 42,
"currency": "USD",
})
# Event with context
analytics.track("ticket_opened", {
"category": "billing",
"priority": "high",
"guild_id": str(interaction.guild_id),
})Props best practices
- Use
string,number, andbooleanvalues for best filterability in Explore. - Keep property keys consistent across events for meaningful cross-event analysis.
- Avoid deeply nested objects -- they are serialized but not directly filterable.
captureError
Explicitly reports an error event. Use this for errors you catch yourself, as opposed to auto-captured command_error events.
analytics.captureError(
error: unknown,
context?: Record<string, unknown>
): voidanalytics.capture_error(
exc: BaseException,
context: dict[str, Any] | None = None,
) -> None| Parameter | Type | Required | Description |
|---|---|---|---|
error / exc | unknown / BaseException | Yes | The error to report. Accepts Error instances, strings, or any value. |
context | Record<string, unknown> / dict | No | Additional context. Special keys: guildId, channelId, userId are extracted as top-level dimensions. |
Returns: void
Never throws. Error fields are size-capped: name (200 chars), message (2 KiB), stack (8 KiB). The source field is automatically set to captureError.
Examples
// Basic error capture
try {
await riskyOperation();
} catch (err) {
analytics.captureError(err);
}
// With context dimensions
analytics.captureError(err, {
userId: interaction.user.id,
guildId: interaction.guildId,
channelId: interaction.channelId,
operation: 'database_query',
table: 'users',
});# Basic error capture
try:
await risky_operation()
except Exception as exc:
analytics.capture_error(exc)
# With context dimensions
analytics.capture_error(exc, {
"user_id": interaction.user.id,
"guild_id": interaction.guild_id,
"channel_id": interaction.channel_id,
"operation": "database_query",
"table": "users",
})Context key behavior
| Context Key | Behavior |
|---|---|
guildId | Extracted as top-level event dimension (filterable by server) |
channelId | Extracted as top-level event dimension (filterable by channel) |
userId | Extracted as top-level event dimension (filterable by user) |
| All other keys | Stored in data.context as additional metadata |
flush
Force-sends all queued events immediately. Resolves when the queue is drained.
analytics.flush(): Promise<void>await analytics.flush() -> NoneReturns: Promise<void> -- resolves when all queued events have been sent (or dropped after exhausting retries). Never rejects.
When to Use
Normally unnecessary. The SDK auto-flushes on two triggers:
- Timer: every 5 seconds (configurable via
flushIntervalMs) - High-water mark: when 50 events are queued
Use flush() explicitly when you need to guarantee delivery before a specific point:
// Ensure all events are sent before a graceful restart
analytics.track('maintenance_start', { reason: 'deploy' });
await analytics.flush();
process.exit(0);# Ensure all events are sent before a graceful restart
analytics.track("maintenance_start", {"reason": "deploy"})
await analytics.flush()
sys.exit(0)shutdown
Detaches all listeners, clears all timers, and performs a final flush with a 3-second timeout. Safe to call multiple times.
analytics.shutdown(): Promise<void>await analytics.shutdown() -> NoneReturns: Promise<void> -- resolves when detachment and final flush are complete. Never rejects.
What It Does
- Closes open voice sessions (emitting final
action_voice_sessionevents) - Flushes remaining gateway event counters
- Stops the snapshot loop
- Stops REST usage tracking
- Detaches REST method patches
- Removes all event listeners from the client
- Removes process signal hooks (
SIGINT,SIGTERM,beforeExit) - Stops the transport flush timer
- Performs a final flush (up to 3 seconds)
Automatic Signal Handling
The SDK auto-registers handlers for SIGINT, SIGTERM, and beforeExit that trigger a flush with a 3-second timeout. After flushing, the signal is re-raised so the default termination behavior proceeds.
// Usually you don't need to call shutdown() manually.
// The SDK handles SIGINT/SIGTERM automatically.
// But if you need to re-attach to a new client:
await analytics.shutdown();
const newAnalytics = createDicolytics(newClient, { apiKey: '...' });Timer cleanup
All SDK timers use unref(), so the SDK never keeps your Node.js process alive. If all other work is done, the process exits naturally without needing to call shutdown().
DicolyticsOptions
The options object passed to createDicolytics or the Dicolytics constructor. See Configuration for detailed descriptions and examples of each option.
interface DicolyticsOptions {
apiKey: string;
endpoint?: string;
disabledEvents?: readonly string[];
debug?: boolean;
snapshotIntervalMs?: number;
flushIntervalMs?: number;
requestTimeoutMs?: number;
clusterId?: number;
autoCapturePromiseRejections?: boolean;
}# create_dicolytics() keyword arguments
api_key: str # required
endpoint: str = "https://api.dicolytics.com"
disabled_events: list[str] = []
debug: bool = False
snapshot_interval_ms: int = 60000
flush_interval_ms: int = 5000
request_timeout_ms: int = 10000
cluster_id: int | None = None
auto_capture_exceptions: bool = FalseDelivery Semantics
| Aspect | Behavior |
|---|---|
| Event ID | UUIDv7, minted at enqueue time. Stable across retries for server-side deduplication. |
| Envelope | POST {endpoint}/v1/events with JSON body. Max 500 events / 1 MiB per batch. |
| Flush triggers | At 50 queued events or every 5 seconds. One in-flight request at a time (order-preserving). |
| Retry | 5xx / network errors: full-jitter exponential backoff, max 5 attempts. |
| 429 handling | Honors Retry-After header (default 300s quiet period). heartbeat and guild_snapshot are exempt. |
| Buffer | Max 5,000 events. Oldest dropped first on overflow. |
| Shutdown | Up to 3-second flush on SIGINT/SIGTERM, then re-raises the signal. |
| Timers | All timers use unref() -- the SDK never keeps your process alive. |
See Transport & Retries for the complete transport specification.