vTilt
Why vTiltHow It WorksFeaturesFAQDocs
Docs / Phoenix
Quick startEvent forwarding
MCP server
Guides
OverviewAuthenticationOAuthAgent skills (prompts)AI intelligenceGoogle Ads
Client setup
CursorClaude DesktopVS CodeCodex
Realtime
Debug ViewRealtime Dashboard
Integration guides
Frontend frameworks
Next.jsNuxt.jsVue.jsReactReact RouterRemixGatsbySvelte / SvelteKitAstroAngularTanStack StartDocusaurus
Backend frameworks
NestJSHonoCloudflare WorkersDjangoFlaskLaravelPhoenixRuby on Rails
Backend languages
PythonPHPRubyElixirGoJava.NET / C#Rust
Stack guides
Vue + PHP
SDK
Browser SDK
InstallScript bundlesEvent trackingAutocaptureIdentify & aliasWeb VitalsSession recordingChat widgetFeature readinessRemote configurationReverse proxyDebug logging
Node SDK
Install & setupCapture, identify & aliasContext & shutdown

Documentation

vTilt
Quick startEvent forwarding

MCP server

Realtime

Debug ViewRealtime Dashboard

Integration guides

NestJSHonoCloudflare WorkersDjangoFlaskLaravelPhoenixRuby on Rails

SDK

DocsIntegration guidesPhoenix

Phoenix

Integrate vTilt with Phoenix (Elixir) — render the SDK in root.html.heex, identify on every authenticated request, send server-side events from a HTTPoison/Req helper.

There's no native Elixir SDK yet, but vTilt's wire format is a plain JSON POST so any HTTP client (Req, HTTPoison, Finch) can emit events. The pattern below renders the Browser SDK from your Phoenix root layout and adds a thin Elixir module for server-side captures.

#1. Configure runtime

# config/runtime.exs
config :my_app, :vtilt,
  token: System.fetch_env!("VTILT_TOKEN"),
  host: System.get_env("VTILT_HOST") || "https://www.vtilt.com"
elixir

#2. Render the SDK in root.html.heex

Pass the current user from your :browser plug pipeline (assigns[:current_user]) into the layout, then init the SDK and identify when the user is set.

<!-- lib/my_app_web/components/layouts/root.html.heex -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <script>
      window.__vtilt = <%= raw Jason.encode!(%{
        token: Application.get_env(:my_app, :vtilt)[:token],
        host:  Application.get_env(:my_app, :vtilt)[:host],
        user:  if(@current_user, do: %{id: to_string(@current_user.id), email: @current_user.email}, else: nil)
      }) %>;
    </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>{@inner_content}</body>
</html>
heex

Important

Important: Identifying users in the root layout means every authenticated page load identifies — not just login. See Identify & alias for the full model.

#3. Server-side capture module

Add req (or your preferred HTTP client) to mix.exs:

{:req, "~> 0.5"},
{:jason, "~> 1.4"},
elixir

Then create a small wrapper:

# lib/my_app/vtilt.ex
defmodule MyApp.VTilt do
  require Logger

  def capture(distinct_id, event, properties \\ %{}) do
    cfg = Application.get_env(:my_app, :vtilt)

    Task.start(fn ->
      Req.post(
        cfg[:host] <> "/api/e",
        json: %{
          api_key: cfg[:token],
          event: event,
          distinct_id: distinct_id,
          properties: properties
        },
        receive_timeout: 2_000
      )
      |> case do
        {:ok, _} -> :ok
        {:error, e} -> Logger.warning("vtilt capture failed: #{inspect(e)}")
      end
    end)
  end
end
elixir
# lib/my_app_web/controllers/checkout_controller.ex
defmodule MyAppWeb.CheckoutController do
  use MyAppWeb, :controller

  def complete(conn, _params) do
    user = conn.assigns.current_user
    MyApp.VTilt.capture(to_string(user.id), "purchase_completed", %{amount: 99.99})
    json(conn, %{ok: true})
  end
end
elixir

#4. LiveView events

Inside a LiveView, you can emit server-side events on handle_event/handle_info, or push the SDK call to the browser via push_event:

def handle_event("cta_click", _params, socket) do
  {:noreply,
   socket
   |> push_event("vtilt:capture", %{event: "cta_clicked", properties: %{location: "hero"}})}
end
elixir
// assets/js/app.js
window.addEventListener('phx:vtilt:capture', e => {
  vt.capture(e.detail.event, e.detail.properties)
})
javascript

#Next steps

  • Identify & alias — anonymous → known user merge.
  • Event forwarding — fan events out to GA4, Meta CAPI, PostHog.
  • Reverse proxy — block-resistant ingestion.
PreviousLaravelIntegration guidesNextRuby on RailsIntegration guides

On this page

  • 1. Configure runtime
  • 2. Render the SDK in root.html.heex
  • 3. Server-side capture module
  • 4. LiveView events
  • Next steps