Getting Started with Multibuzz
Server-side multi-touch attribution tracking for your application.
Platform Integrations
Using a supported platform? Follow the dedicated guide:
| Platform | Integration Type | Guide |
|---|---|---|
| Shopify | Zero-code (App + Pixel) | Shopify Integration → |
For custom applications, continue with the SDK integration below.
The 4-Call Model
Multibuzz uses a simple 4-call SDK model:
| Method | Purpose | When to Call |
|---|---|---|
| init | Configure SDK | App startup |
| event | Track journey steps | User interactions (page views, add to cart, etc.) |
| conversion | Track revenue outcomes | Purchases, signups, subscriptions |
| identify | Link visitor to user | Login, signup (enables cross-device attribution) |
That's it. Four methods to complete multi-touch attribution.
Quick Start
1. Get Your API Key
- Log in to your dashboard
- Navigate to API Keys
- Click Create API Key
- Choose Test environment for development
- Copy your API key (starts with
sk_test_...)
2. Install the Client Library
# Gemfile
gem 'mbuzz'
# Then run:
bundle install
npm install mbuzz
# or
yarn add mbuzz
pip install mbuzz
# or
uv pip install mbuzz
No installation needed.
Use any HTTP client.
3. Initialize (init)
# config/initializers/mbuzz.rb
Mbuzz.init(
api_key: ENV['MBUZZ_API_KEY'],
debug: Rails.env.development?
)
// app.js or server.js
const mbuzz = require('mbuzz');
mbuzz.init({
apiKey: process.env.MBUZZ_API_KEY,
debug: process.env.NODE_ENV === 'development'
});
# app.py or settings.py
import os
import mbuzz
mbuzz.init(
api_key=os.environ['MBUZZ_API_KEY'],
debug=os.environ.get('FLASK_ENV') == 'development'
)
# Set your API key as environment variable
export MBUZZ_API_KEY=sk_test_your_key_here
# Use in Authorization header
curl -H "Authorization: Bearer $MBUZZ_API_KEY" \
https://mbuzz.co/api/v1/events
⚠️ Security: Never commit API keys to git. Use environment variables.
4. Track Events (event)
Track steps in the customer journey:
# Track user interactions
Mbuzz.event("page_view", url: "/pricing")
Mbuzz.event("add_to_cart", product_id: "SKU-123", price: 49.99)
Mbuzz.event("checkout_started", cart_total: 99.99)
# Group events into funnels for focused analysis
Mbuzz.event("signup_start", funnel: "signup", source: "homepage")
Mbuzz.event("signup_complete", funnel: "signup")
// Track user interactions
mbuzz.event('page_view', { url: '/pricing' });
mbuzz.event('add_to_cart', { productId: 'SKU-123', price: 49.99 });
mbuzz.event('checkout_started', { cartTotal: 99.99 });
// Group events into funnels for focused analysis
mbuzz.event('signup_start', { funnel: 'signup', source: 'homepage' });
mbuzz.event('signup_complete', { funnel: 'signup' });
# Track user interactions
mbuzz.event('page_view', url='/pricing')
mbuzz.event('add_to_cart', product_id='SKU-123', price=49.99)
mbuzz.event('checkout_started', cart_total=99.99)
# Group events into funnels for focused analysis
mbuzz.event('signup_start', funnel='signup', source='homepage')
mbuzz.event('signup_complete', funnel='signup')
curl -X POST https://mbuzz.co/api/v1/events \
-H "Authorization: Bearer $MBUZZ_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"event_type": "add_to_cart",
"visitor_id": "abc123...",
"funnel": "purchase",
"properties": {
"product_id": "SKU-123",
"price": 49.99
}
}'
Core Concepts
Sessions (The Foundation of Attribution)
A session is a single visit to your site with acquisition context. Sessions are the touchpoints used for attribution.
What a session captures:
- UTM parameters (utm_source, utm_medium, utm_campaign, etc.)
- Referrer URL (for organic/referral traffic)
- Channel (derived from UTMs + referrer: paid_search, organic, email, etc.)
- Landing page
Session behavior:
- Auto-created: SDK middleware creates sessions automatically
- 30-minute timeout: New session after 30 min of inactivity
- Non-blocking: Session POST is async (doesn't slow your app)
Why sessions matter: Without sessions, you can't know how a visitor arrived. Sessions make multi-touch attribution possible.
Visitors
Anonymous users tracked via cookies.
- Lifetime: 2 years
- Auto-generated: By SDK (64-char hex, stored in
_mbuzz_vidcookie) - Thread-safe: Works with multi-threaded servers
- Privacy-first: No PII required
A single person may have multiple visitors (multiple devices/browsers).
Users
Known, identified users in your system.
- Cross-device: One user identity across all devices
- Permanent: Persists beyond cookies
- Your ID: Use your application's user identifier
Link visitors to users with the identify() method.
Events
Actions users take in your application.
Required fields:
- event_type - Event name (e.g., "signup", "purchase", "add_to_cart")
- user_id OR visitor_id - At least one identifier required
Recommended fields:
- session_id - Links event to session for attribution
- funnel - Group events into funnels for focused analysis
Optional fields:
- properties - Custom metadata (JSON object)
- timestamp - When event occurred (defaults to now)
Funnels
Group related events into funnels for focused conversion analysis.
Why use funnels?
- Separate signup flow from purchase flow
- Analyze each conversion path independently
- Filter dashboard to specific customer journeys
Example funnels:
# Signup funnel
Mbuzz.event("pricing_view", funnel: "signup")
Mbuzz.event("signup_start", funnel: "signup")
Mbuzz.event("signup_complete", funnel: "signup")
# Purchase funnel
Mbuzz.event("add_to_cart", funnel: "purchase")
Mbuzz.event("checkout_started", funnel: "purchase")
Mbuzz.event("purchase_complete", funnel: "purchase")
// Signup funnel
mbuzz.event('pricing_view', { funnel: 'signup' });
mbuzz.event('signup_start', { funnel: 'signup' });
mbuzz.event('signup_complete', { funnel: 'signup' });
// Purchase funnel
mbuzz.event('add_to_cart', { funnel: 'purchase' });
mbuzz.event('checkout_started', { funnel: 'purchase' });
mbuzz.event('purchase_complete', { funnel: 'purchase' });
# Signup funnel
mbuzz.event('pricing_view', funnel='signup')
mbuzz.event('signup_start', funnel='signup')
mbuzz.event('signup_complete', funnel='signup')
# Purchase funnel
mbuzz.event('add_to_cart', funnel='purchase')
mbuzz.event('checkout_started', funnel='purchase')
mbuzz.event('purchase_complete', funnel='purchase')
# Include funnel in event properties
curl -X POST https://mbuzz.co/api/v1/events \
-H "Authorization: Bearer $MBUZZ_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"events": [{
"event_type": "signup_start",
"visitor_id": "abc123...",
"funnel": "signup"
}]
}'
Dashboard filtering: Use the funnel dropdown to view specific funnels. "All Funnels" shows everything.
Automatic Session Tracking
The SDK includes middleware that automatically creates sessions when visitors land on your site.
What happens on each request:
1. SDK checks for existing visitor cookie (_mbuzz_vid)
2. SDK checks for existing session cookie (_mbuzz_sid)
3. If new session detected → POST to API with full context
4. Cookies refreshed
Session context captured automatically:
- Full URL (including UTM parameters)
- Referrer URL
- User-Agent
- IP address (for geo lookup)
Session detection:
- New visitor: No _mbuzz_vid cookie → new session
- Returning visitor, new session: _mbuzz_vid exists but _mbuzz_sid missing/expired (30+ min) → new session
- Same session: Both cookies valid → no new session
The API handles:
- UTM parameter extraction
- Referrer analysis
- Channel derivation (paid_search, organic, email, etc.)
- Visitor/Session record creation
Important: Session POSTs are async and non-blocking. Your page render is not slowed.
Disable if needed:
# config/initializers/mbuzz.rb
Mbuzz.init(
api_key: ENV['MBUZZ_API_KEY'],
skip_paths: ["/health", "/admin"] # Skip specific paths
)
// app.js or server.js
mbuzz.init({
apiKey: process.env.MBUZZ_API_KEY,
skipPaths: ['/health', '/admin'] // Skip specific paths
});
# app.py
mbuzz.init(
api_key=os.environ['MBUZZ_API_KEY'],
skip_paths=['/health', '/admin'] # Skip specific paths
)
N/A - Session tracking is SDK-only.
REST API users manage their own sessions.
User Identification (identify)
Link visitors to known users and store traits. This enables cross-device attribution.
# On signup or login - links visitor automatically
Mbuzz.identify(current_user.id,
traits: {
email: current_user.email,
name: current_user.name,
plan: current_user.plan
}
)
// On signup or login - links visitor automatically
mbuzz.identify(user.id, {
traits: {
email: user.email,
name: user.name,
plan: user.plan
}
});
# On signup or login - links visitor automatically
mbuzz.identify(user.id, traits={
'email': user.email,
'name': user.name,
'plan': user.plan
})
curl -X POST https://mbuzz.co/api/v1/identify \
-H "Authorization: Bearer $MBUZZ_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"user_id": "123",
"visitor_id": "abc123def456...",
"traits": {
"email": "user@example.com",
"name": "Jane Doe",
"plan": "pro"
}
}'
When to call:
- On signup (links first visitor to new user)
- On every login (links new devices to existing user)
- When user attributes change
What Identify Does
identify performs two actions in one call:
- Links visitor to user (if visitor_id provided or available from cookie)
- Stores user traits (email, name, plan, etc.)
When you link a visitor:
Backward attribution:
- All past sessions from this visitor are attributed to the user
- Sessions from before signup become part of their journey
Forward attribution:
- All future sessions from this visitor automatically attributed to the user
Retroactive recalculation:
- If the user has existing conversions, and the newly-linked visitor has sessions
within those conversions' lookback windows, attribution is automatically recalculated
Cross-Device Attribution
Users often visit on multiple devices. Each device has a different visitor ID:
Desktop visitor (abc) ─────┐
├──→ User (usr_123)
Mobile visitor (def) ──────┘
How it works:
1. User visits on desktop → visitor abc created
2. User visits on mobile → visitor def created
3. User signs up on mobile → identify() links mobile visitor
4. User logs in on desktop → identify() links desktop visitor
5. Now both visitors linked → full cross-device attribution
Call identify on every login, not just signup. This ensures visitors from new devices get linked.
Example: Full Journey Attribution
Day 1: Jane visits via Google Ads (desktop) → Session A
Day 3: Jane visits via Facebook (mobile) → Session B
Day 5: Jane signs up (mobile) → identify() links mobile visitor
Day 6: Jane logs in (desktop) → identify() links desktop visitor
Day 7: Jane purchases → Attribution spans ALL sessions (A, B, etc.)
Without identify on each device, you'd only see mobile sessions.
Conversion Tracking
Conversions are the key metric for attribution. When a user converts (signup, purchase, etc.),
the system automatically calculates attribution across all your configured models.
Creating Conversions
You can create conversions in two ways:
Option A: Event-based (recommended for precise attribution)
# First track the conversion event
result = Mbuzz::Client.track(
visitor_id: mbuzz_visitor_id,
event_type: 'checkout_completed',
properties: { order_id: order.id }
)
# Then create the conversion linked to that event
if result[:success]
conversion = Mbuzz::Client.conversion(
event_id: result[:event_id],
conversion_type: 'purchase',
revenue: order.total
)
# Access attribution data
if conversion[:success]
conversion[:attribution][:models].each do |model, credits|
puts "#{model}: #{credits.map { |c| c[:channel] }.join(', ')}"
end
end
end
// First track the conversion event
const result = await mbuzz.event('checkout_completed', {
orderId: order.id
});
// Then create the conversion linked to that event
if (result.success) {
const conversion = await mbuzz.conversion('purchase', {
eventId: result.eventId,
revenue: order.total
});
// Access attribution data
if (conversion.success) {
for (const [model, credits] of Object.entries(conversion.attribution.models)) {
console.log(`${model}: ${credits.map(c => c.channel).join(', ')}`);
}
}
}
# First track the conversion event
result = mbuzz.event('checkout_completed', order_id=order.id)
# Then create the conversion linked to that event
if result.success:
conversion = mbuzz.conversion('purchase',
event_id=result.event_id,
revenue=order.total
)
# Access attribution data
if conversion.success:
for model, credits in conversion.attribution['models'].items():
channels = ', '.join(c['channel'] for c in credits)
print(f"{model}: {channels}")
# Create conversion from event
curl -X POST https://mbuzz.co/api/v1/conversions \
-H "Authorization: Bearer $MBUZZ_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"conversion": {
"event_id": "evt_abc123",
"conversion_type": "purchase",
"revenue": 99.99
}
}'
Option B: Visitor-based (simpler, for direct conversions)
# Create conversion directly from visitor ID
# (uses visitor's most recent session)
conversion = Mbuzz::Client.conversion(
visitor_id: mbuzz_visitor_id,
conversion_type: 'purchase',
revenue: order.total,
funnel: 'purchase', # Optional: group into funnel
properties: { plan: 'pro' }
)
// Create conversion directly from visitor ID
// (uses visitor's most recent session)
const conversion = await mbuzz.conversion('purchase', {
visitorId: mbuzzVisitorId,
revenue: order.total,
funnel: 'purchase', // Optional: group into funnel
properties: { plan: 'pro' }
});
# Create conversion directly from visitor ID
# (uses visitor's most recent session)
conversion = mbuzz.conversion('purchase',
revenue=order.total,
funnel='purchase', # Optional: group into funnel
properties={'plan': 'pro'}
)
# Create conversion from visitor ID
curl -X POST https://mbuzz.co/api/v1/conversions \
-H "Authorization: Bearer $MBUZZ_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"conversion": {
"visitor_id": "65dabef8d611f332d5bb88f5d6870c733d89f962594575b66f0e1de1ede1ebf0",
"conversion_type": "purchase",
"funnel": "purchase",
"revenue": 99.99
}
}'
Conversion Response
Successful conversions return attribution data immediately:
{
"id": "conv_xyz789",
"conversion_type": "purchase",
"revenue": "99.99",
"attribution": {
"lookback_days": 30,
"sessions_analyzed": 3,
"models": {
"first_touch": [
{ "channel": "organic_search", "credit": 1.0, "revenue_credit": "99.99" }
],
"last_touch": [
{ "channel": "email", "credit": 1.0, "revenue_credit": "99.99" }
],
"linear": [
{ "channel": "organic_search", "credit": 0.333, "revenue_credit": "33.00" },
{ "channel": "paid_social", "credit": 0.333, "revenue_credit": "33.00" },
{ "channel": "email", "credit": 0.334, "revenue_credit": "33.99" }
]
}
}
}
When to Use Each Approach
| Approach | Use Case |
|---|---|
Event-based (event_id) |
Tie conversion to specific action (checkout button click, form submit) |
Visitor-based (visitor_id) |
Direct conversions, offline imports, webhook integrations |
Acquisition vs Recurring Revenue
For SaaS and subscription businesses, you need to distinguish between:
- Acquisition: The first conversion (signup, first purchase) - this gets full attribution
- Recurring Revenue: Subsequent payments - these inherit attribution from acquisition
Why this matters: You want to know which channels drive new customers, not just which channels happened to be active when a subscription renewed.
# 1. Mark signup as the acquisition moment
Mbuzz.conversion('signup',
user_id: user.id,
is_acquisition: true
)
# 2. Monthly subscription payments inherit acquisition attribution
Mbuzz.conversion('payment',
user_id: user.id,
revenue: 49.00,
inherit_acquisition: true
)
// 1. Mark signup as the acquisition moment
mbuzz.conversion('signup', {
userId: user.id,
isAcquisition: true
});
// 2. Monthly subscription payments inherit acquisition attribution
mbuzz.conversion('payment', {
userId: user.id,
revenue: 49.00,
inheritAcquisition: true
});
# 1. Mark signup as the acquisition moment
mbuzz.conversion('signup',
user_id=user.id,
is_acquisition=True
)
# 2. Monthly subscription payments inherit acquisition attribution
mbuzz.conversion('payment',
user_id=user.id,
revenue=49.00,
inherit_acquisition=True
)
# 1. Acquisition conversion
curl -X POST https://mbuzz.co/api/v1/conversions \
-H "Authorization: Bearer $MBUZZ_API_KEY" \
-d '{"conversion": {"conversion_type": "signup", "user_id": "123", "is_acquisition": true}}'
# 2. Recurring revenue
curl -X POST https://mbuzz.co/api/v1/conversions \
-H "Authorization: Bearer $MBUZZ_API_KEY" \
-d '{"conversion": {"conversion_type": "payment", "user_id": "123", "revenue": 49.00, "inherit_acquisition": true}}'
How it works:
1. User signs up → is_acquisition: true stores the attribution (channels that drove signup)
2. User pays monthly → inherit_acquisition: true copies attribution from signup
3. All revenue credited to the channels that acquired the customer
When to use:
- is_acquisition: true - First meaningful conversion (signup, first purchase, trial start)
- inherit_acquisition: true - All subsequent revenue from that customer
Common Use Cases
E-Commerce Tracking
class OrdersController < ApplicationController
def create
@order = current_user.orders.create!(order_params)
# Track the event
Mbuzz.event("checkout_completed", order_id: @order.id)
# Record the conversion with revenue
Mbuzz.conversion("purchase",
revenue: @order.total,
order_id: @order.id
)
redirect_to order_path(@order)
end
end
app.post('/orders', async (req, res) => {
const order = await Order.create(req.body);
// Track the event
mbuzz.event('checkout_completed', { orderId: order.id });
// Record the conversion with revenue
mbuzz.conversion('purchase', {
revenue: order.total,
orderId: order.id
});
res.redirect(`/orders/${order.id}`);
});
@app.route('/orders', methods=['POST'])
def create_order():
order = Order.create(**request.form)
# Track the event
mbuzz.event('checkout_completed', order_id=order.id)
# Record the conversion with revenue
mbuzz.conversion('purchase',
revenue=order.total,
properties={'order_id': order.id}
)
return redirect(url_for('order', id=order.id))
curl -X POST https://mbuzz.co/api/v1/events \
-H "Authorization: Bearer $MBUZZ_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"user_id": "123",
"event_type": "Purchase Completed",
"properties": {
"order_id": "order_456",
"amount": 99.99,
"items": 2,
"currency": "USD"
}
}'
SaaS Subscription Tracking
class SubscriptionsController < ApplicationController
def create
@subscription = current_user.create_subscription!(
plan: params[:plan]
)
# Record as conversion with MRR
Mbuzz.conversion("subscription",
revenue: @subscription.monthly_revenue,
plan: @subscription.plan,
billing_cycle: @subscription.billing_cycle
)
redirect_to dashboard_path
end
end
app.post('/subscriptions', async (req, res) => {
const subscription = await user.createSubscription({
plan: req.body.plan
});
// Record as conversion with MRR
mbuzz.conversion('subscription', {
revenue: subscription.monthlyRevenue,
plan: subscription.plan,
billingCycle: subscription.billingCycle
});
res.redirect('/dashboard');
});
@app.route('/subscriptions', methods=['POST'])
def create_subscription():
subscription = current_user.create_subscription(
plan=request.form['plan']
)
# Record as conversion with MRR
mbuzz.conversion('subscription',
revenue=subscription.monthly_revenue,
properties={
'plan': subscription.plan,
'billing_cycle': subscription.billing_cycle
}
)
return redirect(url_for('dashboard'))
curl -X POST https://mbuzz.co/api/v1/events \
-H "Authorization: Bearer $MBUZZ_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"user_id": "123",
"event_type": "Subscription Created",
"properties": {
"plan": "pro",
"billing_cycle": "monthly",
"mrr": 99.00
}
}'
Error Handling
Client libraries never raise exceptions. All errors are:
- Logged (if debug mode enabled)
- Returned as
falsefrom tracking methods - Silently ignored otherwise
Why: Tracking failures should never break your application.
Debugging:
# Enable debug mode in initializer
Mbuzz.init(api_key: ENV['MBUZZ_API_KEY'], debug: true)
# Check return values
result = Mbuzz.event("test_event", foo: "bar")
if result
puts "Tracking succeeded"
else
puts "Tracking failed (check logs)"
end
// Enable debug mode in initializer
mbuzz.init({ apiKey: process.env.MBUZZ_API_KEY, debug: true });
// Check return values
const result = await mbuzz.event('test_event', { foo: 'bar' });
if (result) {
console.log('Tracking succeeded');
} else {
console.log('Tracking failed (check logs)');
}
# Enable debug mode in initializer
mbuzz.init(api_key=os.environ['MBUZZ_API_KEY'], debug=True)
# Check return values
result = mbuzz.event('test_event', foo='bar')
if result.success:
print('Tracking succeeded')
else:
print('Tracking failed (check logs)')
# Check HTTP response codes
curl -v -X POST https://mbuzz.co/api/v1/events \
-H "Authorization: Bearer $MBUZZ_API_KEY" \
-H "Content-Type: application/json" \
-d '{"events": [{"event_type": "test", "visitor_id": "abc123"}]}'
# 2xx = success, 4xx/5xx = failure
Testing
Disable Tracking in Tests
# config/initializers/mbuzz.rb
Mbuzz.init(
api_key: ENV['MBUZZ_API_KEY'],
enabled: !Rails.env.test?
)
// app.js or server.js
mbuzz.init({
apiKey: process.env.MBUZZ_API_KEY,
enabled: process.env.NODE_ENV !== 'test'
});
# app.py or settings.py
mbuzz.init(
api_key=os.environ['MBUZZ_API_KEY'],
enabled=os.environ.get('FLASK_ENV') != 'testing'
)
# Use test API key for development
export MBUZZ_API_KEY=sk_test_your_key_here
Next Steps
Now that you understand the 4-call model:
- Initialize - Set up
init()in your app - Track events - Call
event()for user interactions - Record conversions - Call
conversion()for revenue events - Identify users - Call
identify()on signup/login (enables cross-device) - View attribution - Check your dashboard for attribution data
Need help? Check out:
- Authentication - API key management