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"#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>#3. Server-side capture module
Add req (or your preferred HTTP client) to mix.exs:
{:req, "~> 0.5"},
{:jason, "~> 1.4"},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# 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#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// assets/js/app.js
window.addEventListener('phx:vtilt:capture', e => {
vt.capture(e.detail.event, e.detail.properties)
})#Next steps
- Identify & alias — anonymous → known user merge.
- Event forwarding — fan events out to GA4, Meta CAPI, PostHog.
- Reverse proxy — block-resistant ingestion.