Documentation
Complete guide to integrating Serla analytics into your application.
Getting Started
Serla is a privacy-focused analytics platform designed for developers. No cookies, no fingerprinting, no consent banners required.
Quick Start
- Create an account at serla.dev/signup
- Create a project and copy your API key from the dashboard
- Install the SDK or use the REST API directly
- Start tracking events with a single line of code
Your First Event
const serla = new Serla('sk_live_your_api_key');serla.track('page_view', { page: '/home' });
Authentication
All API requests require authentication using an API key.
API Key Format
sk_live_Live keys - use in productionsk_test_Test keys - events marked as test dataUsing Your API Key
curl -X POST https://serla.dev/api/v1/events \-H "Authorization: Bearer sk_live_your_api_key" \-H "Content-Type: application/json" \-d '{"name": "test_event"}'
Security Best Practices
- Never expose API keys in client-side code for web apps (use a proxy)
- Rotate keys periodically from the dashboard
- Use test keys during development
- Set up IP allowlists for server-side integrations
Installation
Download the SDK for your preferred language. Each SDK is a single file with no dependencies.
Browser Installation
<script src="/serla.js"></script><script>const serla = new Serla('sk_live_your_api_key');serla.trackPageView();</script>
ES Modules
import { Serla } from './serla.js';const serla = new Serla('sk_live_your_api_key');
SDK Configuration
Customize SDK behavior with configuration options.
const serla = new Serla('sk_live_your_api_key', {// API endpoint (default: https://serla.dev/api/v1)endpoint: 'https://serla.dev/api/v1',// Enable debug logging (default: false)debug: true,// Batch size before auto-flush (default: 10)batchSize: 10,// Flush interval in ms (default: 5000)flushInterval: 5000,// Auto-track page views (default: false)autoTrackPageViews: true,// Respect Do Not Track header (default: true)respectDoNotTrack: true,// Session timeout in minutes (default: 30)sessionTimeout: 30});
SDK Methods
| Method | Description |
|---|---|
| track(name, options?) | Queue event for batched sending |
| send(name, options?) | Send event immediately (no batching) |
| identify(userId, props?) | Identify a user |
| trackPageView(props?) | Track a page view |
| flush() | Send all queued events |
| reset() | Clear user identity and session |
| getSessionId() | Get current session ID |
| getUserId() | Get current user ID |
| setDebug(enabled) | Enable/disable debug mode |
track() vs send()
track()Queues events locally and sends them in batches. More efficient for high-volume tracking. Events are sent when the batch size is reached (default: 10) or flush interval elapses (default: 5 seconds).
send()Sends a single event immediately without batching. Use for critical events that must be recorded instantly, like purchases or signups.
// Batched - efficient for high volumeserla.track('page_view', { page: '/home' });serla.track('button_click', { button: 'cta' });// Immediate - for critical eventsawait serla.send('purchase', {distinctId: 'user_123',properties: { revenue: 99.99 }});
Tracking Events
Track any user action with custom properties. Events are the core of Serla analytics.
Basic Event
// Simple eventserla.track('button_click');// Event with propertiesserla.track('purchase', {properties: {product_id: 'prod_123',product_name: 'Pro Plan',price: 49.99,currency: 'USD'}});// Event with user IDserla.track('signup', {distinctId: 'user_456',properties: {plan: 'pro',source: 'google_ads'}});
Revenue Tracking
Serla automatically recognizes and aggregates these revenue properties: revenue, amount, value, price
serla.track('purchase_completed', {distinctId: 'user_123',properties: {revenue: 149.99,quantity: 2,product: 'Pro Plan',currency: 'USD'}});
Tracking Best Practices
- Use snake_case for event names:
button_click, notbuttonClick - Be consistent with naming across your app
- Keep properties flat - avoid deeply nested objects
- Use standard names for common events:
signup,login,purchase
Identifying Users
Associate events with a user to track them across sessions and devices.
// Basic identificationserla.identify('user_123', {email: 'john@example.com',name: 'John Doe'});// Full user profileserla.identify('user_123', {// Contact infoemail: 'john@example.com',name: 'John Doe',phone: '+1234567890',// Account infoplan: 'pro',created_at: '2024-01-15T10:30:00Z',// Company info (B2B)company: 'Acme Inc',company_size: '50-100',industry: 'Technology',// Custom propertieslifetime_value: 499.99});
Reset Identity
Clear user identity on logout:
serla.reset();// Generates new anonymous session ID
Sessions & Page Views
Sessions are created automatically and track user activity over time.
How Sessions Work
- Sessions are created automatically on first event
- Sessions expire after 30 minutes of inactivity (configurable)
- Session ID format:
sess_abc123... - New session created after timeout,
reset(), or new browser tab
Tracking Page Views
// Basic page viewserla.trackPageView();// With custom propertiesserla.trackPageView({title: 'Pricing Page',section: 'marketing'});
SPA Integration
import { useLocation } from 'react-router-dom';function App() {const location = useLocation();useEffect(() => {serla.trackPageView();}, [location.pathname]);}
Properties & Schema
Understanding the data schema and auto-enriched properties.
Reserved Properties
These properties are auto-detected from the request:
| Property | Description |
|---|---|
| $browser | Browser name |
| $os | Operating system |
| $device | Device type (desktop, mobile, tablet) |
| $country | Country code |
| $city | City name |
| $referrer | Referrer URL |
| $utm_source | UTM source |
| $utm_medium | UTM medium |
| $utm_campaign | UTM campaign |
Property Limits
- Max 100 properties per event
- Property names: max 100 characters
- String values: max 1000 characters
- Arrays: max 100 items
- Nested objects: max 3 levels deep
Batch Events
Events are automatically batched for efficiency. The queue flushes when batch size is reached, interval elapsed, or manually.
Manual Flush
// Send all queued events immediatelyawait serla.flush();// Flush before page unload (handled automatically)window.addEventListener('beforeunload', () => {serla.flush();});
API Batch Endpoint
curl -X POST https://serla.dev/api/v1/events/batch \-H "Authorization: Bearer sk_live_..." \-H "Content-Type: application/json" \-d '{"events": [{ "name": "page_view", "properties": { "page": "/home" } },{ "name": "button_click", "properties": { "button": "cta" } }]}'
API Reference
Base URL: https://serla.dev/api/v1
/eventsTrack a single event.
Request
{"name": "purchase","distinctId": "user_123","sessionId": "sess_abc","timestamp": "2024-01-15T10:30:00Z","properties": {"product_id": "prod_123","price": 49.99}}
Response
{"success": true,"eventId": "evt_abc123","sessionId": "sess_abc"}
/events/batchTrack multiple events (max 100 per request).
Request
{"events": [{ "name": "page_view", "properties": { "page": "/home" } },{ "name": "button_click", "properties": { "button": "cta" } }]}
Response
{"success": true,"count": 2,"eventIds": ["evt_abc", "evt_def"]}
/identifyIdentify a user and set properties.
Request
{"distinctId": "user_123","properties": {"email": "john@example.com","name": "John Doe","plan": "pro"}}
/exportExport events as JSON or CSV.
Query Parameters
formatjson or csv (default: json)startDateISO 8601 dateendDateISO 8601 dateeventNameFilter by event namelimitMax results (default: 1000, max: 10000)/users/:distinctIdDelete all data for a user (GDPR right to deletion).
Example
curl -X DELETE https://serla.dev/api/v1/users/user_123 \-H "Authorization: Bearer sk_live_..."
Rate Limits
Limits by Plan
| Plan | Events/sec | Events/month |
|---|---|---|
| Free | 10 | 25,000 |
| Hobby | 50 | 500,000 |
| Pro | 200 | 2,500,000 |
| Max | 1000 | Unlimited |
Rate Limit Headers
X-RateLimit-Limit: 50X-RateLimit-Remaining: 49X-RateLimit-Reset: 1705312800
Handling 429 Errors
async function trackWithRetry(event, maxRetries = 3) {for (let i = 0; i < maxRetries; i++) {try {return await serla.track(event);} catch (error) {if (error.status === 429) {const delay = Math.pow(2, i) * 1000;await new Promise(r => setTimeout(r, delay));} else {throw error;}}}}
Webhooks
Receive real-time notifications when events occur.
Setup
- Go to Dashboard > Settings > Webhooks
- Click "Add Webhook"
- Enter your endpoint URL
- Select events to receive
- Copy the signing secret
Webhook Payload
{"id": "wh_abc123","type": "event.created","timestamp": "2024-01-15T10:30:00Z","data": {"event": {"id": "evt_xyz","name": "purchase","distinctId": "user_123","properties": { "price": 49.99 }}}}
Webhook Events
| Event | Description |
|---|---|
| event.created | New event tracked |
| user.identified | User identified |
| goal.completed | Goal conversion |
| threshold.exceeded | Custom alert triggered |
Signature Verification
const crypto = require('crypto');function verifyWebhook(payload, signature, secret) {const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(payload).digest('hex');return crypto.timingSafeEqual(Buffer.from(signature),Buffer.from(expected));}
Dashboard Features
Funnels
Create conversion funnels to see where users drop off.
Example funnel:
- page_view (page=/pricing)
- signup_started
- signup_completed
- purchase
Goals
Track conversion events and assign monetary values.
- Event-based: Track any event as a conversion
- Pageview-based: Track specific page visits
- Revenue-based: Track events with revenue properties
Retention
Analyze user retention with cohort analysis.
- Group users by signup date
- View retention by day, week, or month
- Define custom retention events
Segments
Create saved segments for filtering data.
Country = United StatesAND Browser = ChromeAND Plan = pro
Attribution
Understand how users find you with attribution models.
- First-touch: Credit first interaction
- Last-touch: Credit last interaction before conversion
- Linear: Equal credit to all touchpoints
- Time-decay: More credit to recent touchpoints
Journeys
Analyze user navigation patterns through your site.
- User paths: See top navigation patterns
- Drop-off points: Identify where users leave
- Session analysis: Understand user flow
Built automatically from page views. Enable autoTrackPageViews: true or call trackPageView().
Privacy & Compliance
No Cookies
Serla does not use cookies. Session tracking uses in-memory storage or optional localStorage.
No Fingerprinting
We never fingerprint users. Identification is explicit via identify() or session-based for anonymous users.
IP Addresses
IPs are used for geolocation only and are never stored. Geolocation is resolved at ingestion time.
Data Retention
| Plan | Retention |
|---|---|
| Free | 7 days |
| Hobby | 60 days |
| Pro | 180 days |
| Max | 3 years |
User Opt-Out
// Check opt-out before initializingif (!localStorage.getItem('serla_optout')) {const serla = new Serla('sk_live_...');}// Opt-out function for privacy settingsfunction optOut() {localStorage.setItem('serla_optout', 'true');serla.reset();}
Error Handling
Error Codes
| Code | Status | Description |
|---|---|---|
| invalid_request | 400 | Malformed request body |
| unauthorized | 401 | Invalid or missing API key |
| forbidden | 403 | Key does not have permission |
| not_found | 404 | Resource not found |
| rate_limit_exceeded | 429 | Too many requests |
| internal_error | 500 | Server error |
SDK Error Handling
try {await serla.track('event');} catch (error) {if (error.code === 'rate_limit_exceeded') {// Wait and retry} else if (error.code === 'unauthorized') {// Check API key} else {console.error('Serla error:', error.message);}}
Debug Mode
const serla = new Serla('sk_live_...', { debug: true });// Or toggle at runtimeserla.setDebug(true);