vTilt
Why vTiltHow It WorksFeaturesFAQDocs
Docs / Rust
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

PythonPHPRubyElixirGoJava.NET / C#Rust

SDK

DocsIntegration guidesRust

Rust

Send vTilt events from Rust code (Axum, Actix, Rocket, plain Tokio) by POSTing to the ingest API.

There's no native Rust SDK yet, but vTilt's wire format is a plain JSON POST so any Rust code can emit events. The pattern below uses reqwest + serde_json; the same shape works inside Axum, Actix, Rocket, Tower, and Tokio binaries.

#1. Add dependencies

# Cargo.toml
[dependencies]
reqwest = { version = "0.12", features = ["json", "rustls-tls"], default-features = false }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
tracing = "0.1"
once_cell = "1"
toml

#2. Capture client

A OnceCell singleton built on reqwest::Client. Captures are spawned onto the runtime as fire-and-forget tasks so handlers never block on the HTTP call.

// src/vtilt.rs
use once_cell::sync::OnceCell;
use serde::Serialize;
use serde_json::Value;
use std::env;
use std::time::Duration;

#[derive(Serialize)]
struct Event<'a> {
    api_key: &'a str,
    event: &'a str,
    distinct_id: &'a str,
    properties: Value,
}

struct Vtilt {
    client: reqwest::Client,
    token: String,
    host: String,
}

static VTILT: OnceCell<Vtilt> = OnceCell::new();

fn vtilt() -> &'static Vtilt {
    VTILT.get_or_init(|| Vtilt {
        client: reqwest::Client::builder()
            .timeout(Duration::from_secs(2))
            .build()
            .expect("vtilt client"),
        token: env::var("VTILT_TOKEN").expect("VTILT_TOKEN missing"),
        host: env::var("VTILT_HOST").unwrap_or_else(|_| "https://www.vtilt.com".to_owned()),
    })
}

pub fn capture(distinct_id: impl Into<String>, event: impl Into<String>, properties: Value) {
    let distinct_id = distinct_id.into();
    let event = event.into();
    tokio::spawn(async move {
        let v = vtilt();
        let body = Event {
            api_key: &v.token,
            event: &event,
            distinct_id: &distinct_id,
            properties,
        };
        if let Err(e) = v.client.post(format!("{}/api/e", v.host)).json(&body).send().await {
            tracing::warn!(?e, "vtilt capture failed");
        }
    });
}

pub fn identify(distinct_id: impl Into<String>, properties: Value) {
    capture(distinct_id, "$identify", serde_json::json!({ "$set": properties }));
}
rust

#3. Capture events

use serde_json::json;

vtilt::identify("user_42", json!({ "email": "alice@example.com", "plan": "pro" }));

vtilt::capture("user_42", "purchase_completed", json!({
    "amount":   99.99,
    "currency": "USD",
}));
rust

#4. Axum handler

use axum::{Json, response::Json as JsonResponse};
use serde::Deserialize;
use serde_json::json;

#[derive(Deserialize)]
struct Checkout { user_id: String, amount: f64 }

async fn checkout(Json(req): Json<Checkout>) -> JsonResponse<serde_json::Value> {
    vtilt::capture(req.user_id, "purchase_completed", json!({ "amount": req.amount }));
    JsonResponse(json!({ "ok": true }))
}
rust

#Next steps

  • Identify & alias — full identification model.
  • Event forwarding — fan events out to GA4, Meta CAPI, PostHog.
  • Reverse proxy — block-resistant ingestion.
Previous.NET / C#Integration guidesNextVue + PHPIntegration guides

On this page

  • 1. Add dependencies
  • 2. Capture client
  • 3. Capture events
  • 4. Axum handler
  • Next steps