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

Laravel

Integrate vTilt with Laravel — render the SDK in your Blade layout, identify on every authenticated request, send server-side events from queued jobs.

For full-stack Laravel apps using Blade (with or without Livewire/Inertia), this guide covers the basics: render the Browser SDK in your layout, identify the authenticated user on every page load, and send server-side events over HTTP.

Tip

Tip: If you specifically combine Laravel with a Vue front-end (Inertia, vue-cli, plain Vite), see the deeper Vue + PHP integration guide.

#1. Configure services

// config/services.php
return [
    'vtilt' => [
        'token' => env('VTILT_TOKEN'),
        'host'  => env('VTILT_HOST', 'https://www.vtilt.com'),
    ],
];
php
# .env
VTILT_TOKEN=YOUR_PROJECT_TOKEN
VTILT_HOST=https://www.vtilt.com
text

#2. Render the SDK in your Blade layout

{{-- resources/views/layouts/app.blade.php --}}
<!doctype html>
<html lang="en">
<head>
  <script>
    window.__vtilt = {
      token: @json(config('services.vtilt.token')),
      host:  @json(config('services.vtilt.host')),
      user:  @json(auth()->check() ? [
        'id'    => (string) auth()->id(),
        'email' => auth()->user()->email,
        'name'  => auth()->user()->name,
      ] : null),
    };
  </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,
        name:  window.__vtilt.user.name,
      });
    }
  </script>
</head>
<body>@yield('content')</body>
</html>
blade

Important

Important: Calling identify in the layout means every authenticated page load identifies — not just the login event. This is the most common integration bug to avoid; see Identify & alias for the full model.

#3. Server-side capture helper

For events that fire outside a browser session (webhooks, scheduled jobs, queued payment callbacks), POST directly to vTilt with Http.

// app/Services/VTilt.php
namespace App\Services;

use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;

class VTilt
{
    public static function capture(string $distinctId, string $event, array $properties = []): void
    {
        try {
            Http::timeout(2)
                ->withHeaders(['Content-Type' => 'application/json'])
                ->post(config('services.vtilt.host') . '/api/e', [
                    'api_key'     => config('services.vtilt.token'),
                    'event'       => $event,
                    'distinct_id' => $distinctId,
                    'properties'  => $properties,
                ]);
        } catch (\Throwable $e) {
            Log::warning('vtilt capture failed', ['e' => $e->getMessage()]);
        }
    }
}
php
// app/Http/Controllers/CheckoutController.php
use App\Services\VTilt;

public function complete(Request $request)
{
    VTilt::capture(
        (string) $request->user()->id,
        'purchase_completed',
        ['amount' => 99.99],
    );
    return response()->json(['ok' => true]);
}
php

#4. Run captures in a queue

Queue server-side captures so analytics never block the user-facing response. Network failures shouldn't surface to your users.

// app/Jobs/CaptureVTiltEvent.php
namespace App\Jobs;

use App\Services\VTilt;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\SerializesModels;

class CaptureVTiltEvent implements ShouldQueue
{
    use Dispatchable, Queueable, SerializesModels;

    public function __construct(
        public string $distinctId,
        public string $event,
        public array $properties = [],
    ) {}

    public function handle(): void
    {
        VTilt::capture($this->distinctId, $this->event, $this->properties);
    }
}
php
CaptureVTiltEvent::dispatch((string) $user->id, 'subscription_renewed', ['plan' => $user->plan]);
php

#Next steps

  • Vue + PHP integration guide — full Vue + Laravel/PHP walkthrough.
  • Identify & alias — anonymous → known user merge.
  • Event forwarding — fan events out to GA4, Meta CAPI, PostHog.
PreviousFlaskIntegration guidesNextPhoenixIntegration guides

On this page

  • 1. Configure services
  • 2. Render the SDK in your Blade layout
  • 3. Server-side capture helper
  • 4. Run captures in a queue
  • Next steps