Skip to content

Custom Events

Track business-specific events beyond the automatic captures using track(), captureError(), and other SDK methods.

track(name, props?)

The primary method for recording custom events. Events are queued, batched, and delivered asynchronously -- track() never throws.

ts
analytics.track('purchase', { sku: 'pro', amount: 42 });
python
analytics.track("purchase", {"sku": "pro", "amount": 42})

Basic Examples

ts
// Simple event with no properties
analytics.track('daily_reward_claimed');

// Event with structured properties
analytics.track('ticket_opened', {
  category: 'billing',
  priority: 'high',
  guildId: interaction.guildId,
});

// Tracking a game result
analytics.track('game_finished', {
  game: 'trivia',
  winner: interaction.user.id,
  playerCount: 4,
  durationMs: 120000,
});

// Feature usage tracking
analytics.track('setting_changed', {
  setting: 'language',
  oldValue: 'en',
  newValue: 'ko',
});
python
# Simple event with no properties
analytics.track("daily_reward_claimed")

# Event with structured properties
analytics.track("ticket_opened", {
    "category": "billing",
    "priority": "high",
    "guild_id": interaction.guild_id,
})

# Tracking a game result
analytics.track("game_finished", {
    "game": "trivia",
    "winner": interaction.user.id,
    "player_count": 4,
    "duration_ms": 120000,
})

# Feature usage tracking
analytics.track("setting_changed", {
    "setting": "language",
    "old_value": "en",
    "new_value": "ko",
})

Properties (props)

Props are arbitrary key-value pairs attached to the event. They appear in the dashboard under Activity > Custom Events and can be used for filtering and grouping in the Explore tool.

Note

  • Use string, number, and boolean values for best filterability.
  • Avoid deeply nested objects -- flat structures are easier to query.
  • Use consistent key names across events for meaningful cross-event analysis.

Limits

ItemLimit
Event name length1 -- 256 characters
Props keys per eventMax 50
Key lengthMax 100 characters
Total props sizeMax 8 KiB serialized
Individual string valueMax 4 KiB

Note

Event names are limited to 256 characters and props must be JSON-serializable. Events that exceed limits are silently truncated or dropped -- track() never throws.

captureError(err, ctx?)

Explicitly report an error. Unlike command_error events (which are auto-captured when interaction replies reject), captureError is for errors you catch yourself.

ts
try {
  await riskyDatabaseOperation();
} catch (err) {
  analytics.captureError(err, {
    userId: interaction.user.id,
    guildId: interaction.guildId,
    operation: 'database_query',
  });
}
python
try:
    await risky_database_operation()
except Exception as exc:
    analytics.capture_error(exc, {
        "user_id": interaction.user.id,
        "guild_id": interaction.guild_id,
        "operation": "database_query",
    })

Error fields are size-capped: name (200 chars), message (2 KiB), stack (8 KiB). The source field is set to captureError to distinguish from auto-captured errors.

The optional context object supports special keys:

KeyEffect
guildIdStored as the event's guildId dimension (filterable)
channelIdStored as the event's channelId dimension
userIdStored as the event's userId dimension
Other keysStored in data.context as additional metadata

flush()

Force-sends all queued events immediately.

ts
await analytics.flush();
python
await analytics.flush()

INFO

Normally unnecessary. The SDK auto-flushes every 5 seconds (configurable via flushIntervalMs) or when 50 events are queued. Use flush() only when you need to guarantee delivery before a specific point in time.

shutdown()

Detaches all listeners, clears timers, and performs a final flush with a 3-second timeout.

ts
await analytics.shutdown();
python
await analytics.shutdown()

The SDK auto-registers SIGINT, SIGTERM, and beforeExit handlers that trigger a flush, so explicit shutdown() calls are usually not needed. Call it manually when you want to detach and re-attach the SDK to a different client, or during graceful shutdown sequences.

Viewing Custom Events in the Dashboard

Custom events appear in several places:

  1. Activity > Custom Events -- dedicated report page with trend charts and a table of recent events.
  2. Explore -- filter by event_type = custom and drill down by data.name or any props.* field.
  3. Overview -- custom event counts contribute to the total event KPI.

Filtering by Props in Explore

In the Explore tool, custom event properties are accessible under props.* field paths:

event_type = custom
props.category = billing
props.priority = high

See Explore for more on the query builder.

Complete Example

ts
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.on('interactionCreate', async (interaction) => {
  if (!interaction.isChatInputCommand()) return;

  // Track command usage with business context
  analytics.track('command_used', {
    command: interaction.commandName,
    guildId: interaction.guildId,
    channel: interaction.channelId,
    isPremium: await checkPremiumStatus(interaction.guildId),
  });

  // Handle the command...
});

// Capture errors explicitly
process.on('unhandledRejection', (err) => {
  analytics.captureError(err, { source: 'unhandledRejection' });
});

client.login(process.env.DISCORD_TOKEN);
python
import os
import discord
from dicolytics import create_dicolytics

bot = discord.Bot()

analytics = create_dicolytics(bot, api_key=os.environ["DICOLYTICS_KEY"])

@bot.event
async def on_application_command(ctx):
    # Track command usage with business context
    analytics.track("command_used", {
        "command": ctx.command.name,
        "guild_id": str(ctx.guild_id),
        "channel": str(ctx.channel_id),
        "is_premium": await check_premium_status(ctx.guild_id),
    })

@bot.event
async def on_error(event, *args, **kwargs):
    import sys
    analytics.capture_error(sys.exc_info()[1], {"source": "on_error"})

bot.run(os.environ["DISCORD_TOKEN"])

Dicolytics — Discord bot analytics