Documentation Index
Fetch the complete documentation index at: https://docs.tell.rs/llms.txt
Use this file to discover all available pages before exploring further.
Tell has three data protocols. Choosing the right one for each signal keeps your data clean and your queries fast.
Events, logs, or snapshots?
| Signal | Protocol | Example |
|---|
| A user does something | Event | sign_up, purchase, page_view |
| Your app does something | Log | Error, warning, deploy, job completion |
| An external service has a metric | Snapshot | GitHub stars, Shopify revenue, Cloudflare requests |
Events are user-centric. Every event is tied to a device and a user. Use events for anything you’d put in a product analytics tool — signups, purchases, feature usage, page views, funnels, retention.
Logs are infrastructure-centric. They’re tied to a service and a severity level, not a user. Use logs for errors, warnings, deploys, background job status — anything your application does that isn’t a direct user action.
Snapshots pull metrics from external services on a schedule. Use snapshots for data that lives outside your app — repository stars, store revenue, email delivery rates.
When it’s unclear
Some signals could go either way. Here’s how to decide:
| Signal | Use event or log? | Why |
|---|
| User clicks “Export” | Event | It’s a user action you want in funnels and retention |
| Export job fails | Log | It’s an application error, not a user action |
| User sees an error page | Log | It’s an application error — count and filter by user in log queries |
| Server returns 500 | Log | It’s infrastructure behavior |
| Payment succeeds | Event (revenue) | It’s a business outcome tied to a user |
| Payment gateway times out | Log | It’s a system failure |
| API rate limit hit | Log | It’s infrastructure, even if a user triggered it |
The test: would a product manager care about this in a funnel or retention chart? If yes, it’s an event. If it’s something an engineer debugs in a log viewer, it’s a log.
Naming events
Use snake_case. Be specific. Name the action, not the UI element.
// Good
tell.track('sign_up', { plan: 'free' });
tell.track('invite_sent', { role: 'editor' });
tell.track('report_exported', { format: 'csv' });
// Bad
tell.track('click_button'); // what button?
tell.track('SignUp'); // use snake_case
tell.track('user_did_a_thing'); // be specific
Naming conventions
| Pattern | Example | When to use |
|---|
noun_verb | report_exported, invite_sent | Completed actions (most events) |
noun_verb | checkout_started, trial_ended | State transitions |
page_view | page_view | Page or screen views (use this exact name) |
feature_used | feature_used with feature property | Generic feature tracking |
Keep your event namespace flat. Don’t use dots or slashes — purchase not ecommerce.purchase. Use properties for dimensions, not the event name.
// Good — one event name, dimensions in properties
tell.track('purchase', { category: 'subscription', plan: 'pro' });
tell.track('purchase', { category: 'one_time', product: 'report' });
// Bad — splitting into multiple event names
tell.track('subscription_purchase', { plan: 'pro' });
tell.track('one_time_purchase', { product: 'report' });
The first approach gives you one funnel step, one retention event, and one metric — broken down by category. The second fragments your data.
Choosing properties
Properties are the key-value pairs that make your events queryable. Every property you send becomes a breakdown, filter, or aggregation dimension.
What to include
| Property type | Examples | Why |
|---|
| What happened | plan, amount, format, source | Core dimensions for breakdowns |
| Where it happened | page_url, screen, section | Location context |
| Experiment context | variant, experiment_id | A/B test analysis |
| Business context | currency, coupon_code, referrer | Revenue and attribution |
What NOT to include
- PII in plain text — use the redact transform or hash values before sending
- High-cardinality IDs — don’t put
request_id or trace_id in event properties (use logs for that)
- Redundant context — the SDK already sends device type, OS, browser, and session ID automatically
Super properties
If you find yourself adding the same property to every track call, register it once:
tell.register({ app_version: '2.1.0', environment: 'production' });
Every subsequent event includes these properties automatically. See Events & Properties for details.
Structuring logs
Logs need a severity level and a message. The service name is set once when you initialize the SDK. Add structured data for anything you’d want to filter or search on.
tell.log('error', 'Payment gateway timeout', {
gateway: 'stripe',
duration_ms: 12340,
retry_count: 3
});
Use a consistent service name for each SDK instance — billing, auth, api, worker. This becomes your primary filter dimension in log queries.
See Logs for severity levels and SDK methods.
A starter tracking plan
Here’s a minimal set of events that covers most SaaS products:
| Event | Properties | Why |
|---|
page_view | url, title, referrer | Traffic and navigation |
sign_up | plan, source, referrer | Acquisition |
login | method | Activation and frequency |
feature_used | feature, context | Engagement |
purchase | plan, amount, currency | Revenue |
invite_sent | role | Virality |
export_created | format, row_count | Value delivery |
Start small. You can always add events later — but renaming or removing them means losing historical continuity.
What’s next