Skip to content

TypeScript SDK

@tickr.gg/sdk is an isomorphic TypeScript client for the Tickr ingestion API. It works in Node 18+ and the browser with no Node-only imports; it relies on globalThis.fetch and globalThis.crypto. It’s the same client the Minecraft plugin bridge uses.

Install

npm install @tickr.gg/sdk
# or: pnpm add @tickr.gg/sdk

Quick start

import { TickrClient } from '@tickr.gg/sdk'

const tickr = new TickrClient({
  serverKey: process.env.TICKR_SERVER_KEY!, // tickr_sk_…
})

// Events are queued and flushed in batches every 5s
// (idempotencyKey + ts are auto-filled when missing).
tickr.track({ name: 'boss_kill', category: 'pve', playerId: 'uuid-123', value: 1 })
tickr.track({
  name: 'shop_purchase',
  category: 'economy',
  playerId: 'uuid-123',
  value: 250,
  properties: { item: 'diamond_sword', currency: 'coins' },
})

// Join/leave sessions.
tickr.session({ playerId: 'uuid-123', username: 'Steve', type: 'join', sessionId: 'sess-1' })

// Graceful exit: stops the flush timer and drains the queues.
process.on('SIGTERM', () => void tickr.shutdown())

Bridge usage (game server loop)

const tickr = new TickrClient({
  serverKey: config.serverKey,
  flushIntervalMs: 5000, // batch window
  maxBatchSize: 50,      // items per request
  maxQueueSize: 10_000,  // bound memory; oldest items dropped beyond this
})

// Heartbeat every 30s — immediate POST, not batched.
setInterval(() => void tickr.heartbeat({ tps: server.currentTps() }), 30_000)

server.on('playerJoin', (p) =>
  tickr.session({ playerId: p.uuid, username: p.name, type: 'join', sessionId: p.sessionId, world: p.world })
)
server.on('playerQuit', (p) =>
  tickr.session({ playerId: p.uuid, type: 'leave', sessionId: p.sessionId, ts: Date.now() })
)

// Gate premium features on the org's plan.
const { premium, plan } = await tickr.checkLicense()

// On graceful stop: tell Tickr it's a clean shutdown
// (absence of this before a drop ⇒ counted as a crash), then flush.
server.on('stop', async () => {
  await tickr.heartbeat({ cleanShutdown: true })
  await tickr.shutdown()
})

Options

OptionDefaultDescription
serverKeynone (required)Server API key, sent as Authorization: Bearer <key>.
baseUrlhttps://api.tickr.ggAPI origin (point at your own deployment if self-hosted).
flushIntervalMs5000Background flush cadence.
maxBatchSize50Max items per ingest request.
maxQueueSize10000Per-queue memory bound (drop-oldest beyond it).
maxRetries5Retries per batch for retryable failures.
fetchglobalThis.fetchInjectable fetch (tests, polyfills).

Methods

  • track(input): queue a custom event (idempotencyKey/ts auto-generated when missing).
  • session(input): queue a join/leave session item.
  • heartbeat({ tps?, cleanShutdown? }): immediate POST /v1/ingest/heartbeat; throws TickrApiError on non-2xx.
  • checkLicense(): GET /v1/ingest/license → { premium, plan }.
  • flush(): drain queues now; concurrent calls share one in-flight flush.
  • shutdown(): stop the timer and run a final flush.

Reliability semantics

  • Events and sessions sit in separate in-memory FIFO queues, bounded by maxQueueSize (oldest items dropped, with a single console.warn).
  • Failed requests are retried with exponential backoff + jitter (500 ms base, ×2, capped at 30 s) up to maxRetries, but only for network errors, 429 and 5xx. Other 4xx responses drop the batch (logged once).
  • After retries are exhausted the batch goes back to the front of the queue (order preserved) and is retried on the next flush.
  • Auto-generated idempotencyKeys mean retried event batches never double-count. See idempotency in the API reference.