Ruby on Rails
Integrate vTilt with Rails — render the SDK in your application layout, identify on every authenticated request, send server-side events from a Net::HTTP helper.
There's no native Ruby SDK yet, but vTilt's wire format is a plain JSON POST so any Ruby HTTP client (Net::HTTP, Faraday, HTTP.rb) can emit events. The pattern below covers rendering the Browser SDK in your Rails layout, identifying the authenticated user on every request, and sending server-side events from a queued job.
#1. Configure credentials
# config/credentials.yml.enc (or environment variables)
vtilt:
token: { { TOKEN } }
host: { { ORIGIN } }yaml
# config/initializers/vtilt.rb
Rails.application.config.vtilt = {
token: Rails.application.credentials.dig(:vtilt, :token) || ENV.fetch('VTILT_TOKEN'),
host: Rails.application.credentials.dig(:vtilt, :host) || ENV.fetch('VTILT_HOST', 'https://www.vtilt.com'),
}ruby
#2. Render the SDK in your application layout
<%# app/views/layouts/application.html.erb %>
<!doctype html>
<html lang="en">
<head>
<script>
window.__vtilt = <%= {
token: Rails.application.config.vtilt[:token],
host: Rails.application.config.vtilt[:host],
user: current_user ? { id: current_user.id.to_s, email: current_user.email } : nil
}.to_json.html_safe %>;
</script>
<script>
!(function(t,e){/* ...vTilt loader snippet from Quick start... */})(document, window.vt || []);
vt.init(window.__vtilt.token, {
api_host: window.__vtilt.host,
autocapture: true,
capture_pageview: true,
capture_pageleave: true,
});
if (window.__vtilt.user) {
vt.identify(window.__vtilt.user.id, { email: window.__vtilt.user.email });
}
</script>
</head>
<body><%= yield %></body>
</html>erb
#3. Server-side capture helper
For events that fire outside a browser session — webhooks, Active Job tasks, server-side conversions — POST directly to vTilt.
# app/services/vtilt.rb
require 'net/http'
require 'json'
class Vtilt
def self.capture(distinct_id:, event:, properties: {})
cfg = Rails.application.config.vtilt
uri = URI.join(cfg[:host], '/api/e')
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https', open_timeout: 2, read_timeout: 2) do |http|
req = Net::HTTP::Post.new(uri.path, 'Content-Type' => 'application/json')
req.body = {
api_key: cfg[:token],
event: event,
distinct_id: distinct_id,
properties: properties,
}.to_json
http.request(req)
end
rescue StandardError => e
Rails.logger.warn("vtilt capture failed: #{e.message}")
end
endruby
# app/controllers/checkouts_controller.rb
class CheckoutsController < ApplicationController
def complete
Vtilt.capture(
distinct_id: current_user.id.to_s,
event: 'purchase_completed',
properties: { amount: 99.99 },
)
render json: { ok: true }
end
endruby
#4. Run captures via Active Job
For low-latency endpoints, push the capture to a background job so analytics never blocks the user-facing response.
# app/jobs/vtilt_capture_job.rb
class VtiltCaptureJob < ApplicationJob
queue_as :default
def perform(distinct_id, event, properties = {})
Vtilt.capture(distinct_id: distinct_id, event: event, properties: properties)
end
endruby
VtiltCaptureJob.perform_later(user.id.to_s, 'subscription_renewed', { plan: user.plan })ruby
#Next steps
- Identify & alias — anonymous → known user merge.
- Event forwarding — fan events out to GA4, Meta CAPI, PostHog.
- Reverse proxy — block-resistant ingestion.