Docs
Guides

Identity and event taxonomy

Choose stable customer identifiers and a maintainable product event contract.

Identity and event quality determine whether every downstream lifecycle, journey, segment, and score is trustworthy. Decide these contracts before building a large model.

Choose the customer boundary

Kite models one customer as one account, workspace, or tenant. Send its stable internal ID as customerId.

Good IDs are immutable and available to every backend producer:

account_42
workspace_01J...
tenant_8f31...

Do not use an email address, mutable slug, display name, browser session, or individual user ID when Customer Success operates at the account level. If a person acts for an account, keep the account as customerId and add a non-sensitive actor ID to event properties only when needed.

IDs are opaque and isolated by project and environment. Kite does not merge aliases or maintain a user-to-account hierarchy.

Identify and track

Use identify for current customer attributes and track for immutable product facts:

await kite.identify({
  customerId: 'account_42',
  traits: {
    plan: 'pro',
    availableFeatures: ['exports', 'sso'],
  },
});

await kite.track({
  customerId: 'account_42',
  event: 'integration.connected',
  properties: { provider: 'salesforce' },
  idempotencyKey: 'integration_987_connected',
});

Traits are shallow-merged, so updating { plan: 'pro' } preserves other existing traits. A track operation creates an unknown customer with empty traits when necessary.

OperationUse it forHistorical behavior
identifyCurrent plan, seats, entitlements, CRM attributesMutates current traits; it is not a replayable historical fact.
trackAccount created, integration connected, report exportedCreates an immutable event with an occurrence timestamp.

The raw API accepts identify only inside POST /v1/events/batch. The SDK handles that detail.

Design event names

Prefer stable, past-tense domain facts:

  • account.created
  • integration.connected
  • report.exported
  • subscription.cancelled

Avoid UI implementation details such as modal_button_clicked, conclusions such as customer.healthy, and values embedded in names. Put dimensions such as provider, feature, and format in properties.

For every event, decide:

ConcernDecision
NameOne namespaced fact with one durable meaning.
ProducerThe backend authoritative for that fact.
CustomerThe account or workspace receiving the outcome.
TimestampWhen it occurred, including an ISO 8601 timezone.
PropertiesBounded dimensions required by a definition or diagnosis.
IdempotencyStable source operation ID when one exists.

Declare the contract

kite.config.ts
import { defineConfig } from '@kitesdk/config';

export default defineConfig({
  project: 'acme',
  strictSchema: true,
  events: {
    'integration.connected': {
      description: 'A workspace completed an integration connection',
      properties: {
        provider: { type: 'string', enum: ['salesforce', 'hubspot'] },
      },
    },
    'report.exported': {
      properties: {
        format: { type: 'string', enum: ['csv', 'pdf'] },
        actorId: { type: 'string', optional: true },
      },
    },
  },
  customerTraits: {
    plan: { type: 'string', optional: true },
    seats: { type: 'number', optional: true },
    availableFeatures: {
      type: 'array',
      items: { type: 'string' },
      optional: true,
    },
  },
});

Properties are required unless optional: true. With strictSchema: true, undeclared events, missing or unknown properties, invalid traits, and wrong types return HTTP 422. With false, Kite accepts violations and writes diagnostics to service logs; it does not expose a durable violation inbox.

Roll strict mode out safely:

  1. Declare the existing producer contract with strictSchema: false.
  2. Deploy to test and exercise every producer.
  3. Fix diagnostics and add producer contract tests.
  4. Enable strictSchema: true in test, then promote the tested version.

Delivery semantics

Use an idempotency key for every retryable track. Repeating the same key and canonical payload returns the original event; changing the customer, event, properties, or explicit timestamp returns 409 IDEMPOTENCY_CONFLICT.

The Node.js SDK generates a key when retries are enabled, but a stable business key also protects against duplicate calls across processes and deployments.

const result = await kite.track({
  customerId: 'account_42',
  event: 'report.exported',
  properties: { format: 'csv' },
  idempotencyKey: 'export_98765',
});

if (!result) {
  // onError has received the delivery failure.
}

SDK delivery methods report failures through onError and resolve to undefined; they do not throw request failures. Batch requests preserve operation order and reject invalid input rather than partially accepting it.

Identity migrations

There is no identity merge command. For a migration:

  1. Choose the canonical ID in the source system.
  2. Stop producers from sending the old ID.
  3. Backfill retained facts under the canonical ID with deterministic idempotency keys.
  4. Run a customer-scoped dry-run and persisted recompute.
  5. Verify the canonical customer before expanding the migration.

Do not copy current traits into historical event properties unless they were true when the event occurred. See Backfill and recomputation for replay semantics.

Governance checklist

  • Assign an owner in your internal catalog and add a DSL description for every event.
  • Emit authoritative backend facts rather than inferred browser clicks.
  • Never silently change the meaning or type of an existing property.
  • Exclude secrets, credentials, and unnecessary personal data.
  • Validate producers in test before promoting schemas to live.

On this page