Server-side multi-touch attribution for Ruby. Track customer journeys, attribute conversions, know which channels drive revenue.
Add to your Gemfile:
gem 'mbuzz'
Then:
bundle install
# config/initializers/mbuzz.rb Mbuzz.init(api_key: ENV['MBUZZ_API_KEY'])
Track steps in the customer journey:
Mbuzz.event("page_view", url: request.url) 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")
Record revenue-generating outcomes:
Mbuzz.conversion("purchase", revenue: 99.99, funnel: "purchase", # Optional: group into funnel order_id: order.id )
Link visitors to known users (enables cross-device attribution):
# On signup or login Mbuzz.identify(current_user.id, traits: { email: current_user.email, name: current_user.name } )
Group related events into funnels for focused conversion analysis in your dashboard.
# 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.conversion("purchase", funnel: "purchase", revenue: 99.99)
Why use funnels?
- Separate signup flow from purchase flow
- Analyze each conversion path independently
- Filter dashboard to specific customer journeys
mbuzz auto-integrates with Rails via Railtie:
- Middleware for visitor and session cookie management
- Controller helpers for convenient access
class ApplicationController < ActionController::Base # Access current IDs mbuzz_visitor_id # Returns the visitor ID from cookie mbuzz_user_id # Returns user_id from session["user_id"] mbuzz_session_id # Returns the session ID from cookie end
Note: For tracking and identification, prefer the main API which auto-enriches events with URL/referrer:
# Recommended - use main API in controllers Mbuzz.event("page_view", page: request.path) Mbuzz.identify(current_user.id, traits: { email: current_user.email })
Access IDs from anywhere in your request cycle:
Mbuzz.visitor_id # Current visitor ID (from cookie) Mbuzz.user_id # Current user ID (from session["user_id"]) Mbuzz.session_id # Current session ID (from cookie)
When tracking from background jobs (Sidekiq, GoodJob, etc.), there's no HTTP request context. Rails 7+ handles this automatically via CurrentAttributes.
mbuzz uses ActiveSupport::CurrentAttributes which Rails automatically serializes into ActiveJob payloads:
# In your controller - just enqueue the job class OrdersController < ApplicationController def create @order = Order.create!(order_params) ProcessOrderJob.perform_later(@order.id) # visitor_id is automatically captured and passed to the job end end # In your job - mbuzz just works! class ProcessOrderJob < ApplicationJob def perform(order_id) order = Order.find(order_id) # Mbuzz::Current.visitor_id was restored by Rails Mbuzz.conversion("purchase", revenue: order.total) end end
How it works:
- Middleware captures
visitor_idfrom cookie intoMbuzz::Current - Controller enqueues job
- Rails serializes
Mbuzz::Currentinto job payload - Job runs → Rails restores
Mbuzz::Current Mbuzz.conversion()reads fromCurrent- works!
For non-Rails apps or when you need more control:
# Store visitor_id on your model class Order < ApplicationRecord before_create { self.mbuzz_visitor_id = Mbuzz.visitor_id } end # Pass explicitly in background job class ProcessOrderJob def perform(order_id) order = Order.find(order_id) Mbuzz.conversion("purchase", visitor_id: order.mbuzz_visitor_id, # Explicit revenue: order.total ) end end
For non-Rails apps, add the middleware manually:
# config.ru or app.rb require 'mbuzz' Mbuzz.init(api_key: ENV['MBUZZ_API_KEY']) use Mbuzz::Middleware::SessionEndpoint # see "Full-page caching" below use Mbuzz::Middleware::Tracking run MyApp
Add this to your layout or nothing is tracked. Since 0.10.0 a page response never sets the
visitor cookie: only POST /_mbuzz/session mints, and the snippet is what calls it.
A page response can be stored by a full-page cache (Cloudflare, Varnish, nginx, Rack::Cache, a
CDN) and replayed to every visitor. A Set-Cookie sitting in that cache hands everyone the
first visitor's id, so unrelated people merge into one journey — corruption rather than loss,
since every row exists and is simply attributed to the wrong person. Minting only on a response
no cache stores is the only way to prevent it.
The same endpoint solves the original problem too: a cached page never enters the Rack stack, so the tracking middleware cannot run — but this one request always reaches it.
Mbuzz::Middleware::SessionEndpoint answers POST /_mbuzz/session. Rails mounts it for you;
Rack and Sinatra apps add it ahead of Tracking as shown above.
Then call it once per page, from your layout:
<script> fetch('/_mbuzz/session', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ url: location.href, referrer: document.referrer || '' }), credentials: 'same-origin', keepalive: true }).catch(function () {}); </script>
Two things to keep as they are:
- Inline the script, don't enqueue a file. Asset optimisers (WP Rocket, LiteSpeed, and the Rails equivalents) delay external scripts until the visitor first interacts. A visitor who lands and converts without clicking anything first would never be established.
credentials: 'same-origin'is required, or the cookie never comes back.
The visitor id is never created or read in JavaScript. It stays HttpOnly and server-set, which
is what preserves its full two-year life — a cookie written by document.cookie is capped at
7 days under Safari's ITP, and 24 hours after an ad click.
Mbuzz.init( api_key: "sk_live_...", # Required - from mbuzz.co dashboard api_url: "https://api.mbuzz.co/api/v1", # Optional - API endpoint debug: false # Optional - enable debug logging )
| Method | When to Use |
|---|---|
init |
Once on app boot |
event |
User interactions, funnel steps |
conversion |
Purchases, signups, any revenue event |
identify |
Login, signup, when you know the user |
mbuzz never raises exceptions. All methods return false on failure and log errors in debug mode.
- Ruby 3.0+
- Rails 6.0+ (for automatic integration) or any Rack app
MIT License