Cookie consent for Laravel

The banner is one tag in your Blade layout, before @vite. The consents your forms ask for — newsletter, terms — are recorded from your controller with one HTTP call, each with a signed receipt.

Set it up with your AI agent

Connect the Croombit MCP server to your editor or coding agent and paste this. It creates the site, the cookie categories and the banner, and edits your code.

Using Croombit: create a site for this project and keep the cookie categories it comes with. Write the banner text and publish the banner. Create a TEST publishable key for localhost:3000. This is a Laravel app: add the cmp.js tag first in <head> of resources/views/layouts/app.blade.php with the key from config('services.croombit.key'), add the croombit entry to config/services.php, and record the newsletter checkbox of our signup controller with Http::post to /v1/consent using CROOMBIT_SECRET_KEY.

The agent works in TEST. Going live takes two steps in the console: verify your domain and swap in a LIVE key — see going live.

Or by hand

1. Keys in config/services.php

// config/services.php
'croombit' => [
    'key' => env('CROOMBIT_PUBLISHABLE_KEY'),   // pk_test_… locally, pk_live_… in production
    'secret' => env('CROOMBIT_SECRET_KEY'),     // sk_… — server only
],

2. Add the tag to your layout

{{-- resources/views/layouts/app.blade.php --}}
<head>
    <meta charset="utf-8">
    <script src="https://croombit.com/cmp.js"
            data-tc-api-key="{{ config('services.croombit.key') }}"></script>
    {{-- …everything else after it --}}
    @vite(['resources/css/app.css', 'resources/js/app.js'])
</head>

3. Hold back trackers

Tags in your Blade views: type="text/plain" plus data-tc-consent. They stay inert until the visitor grants that category.

<script type="text/plain" data-tc-consent="analytics" async
        src="https://www.googletagmanager.com/gtag/js?id=G-XXXX"></script>
<script type="text/plain" data-tc-consent="analytics">
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());
  gtag('config', 'G-XXXX');
</script>

4. Record consents from your controller

A plain POST /v1/consent with your secret key — no SDK needed. The Idempotency-Key makes a retry safe. See Record and query consent.

use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;

// e.g. in the controller that handles your signup form
Http::withToken(config('services.croombit.secret'))
    ->withHeaders(['Idempotency-Key' => (string) Str::uuid()])
    ->post('https://api.croombit.com/v1/consent', [
        'subject' => 'email:' . $user->email,
        'purpose' => 'newsletter',
        'state' => $request->boolean('newsletter') ? 'GRANTED' : 'DENIED',
        'channel' => 'signup_form',
    ])
    ->throw();

Questions

I have a Content Security Policy. What do I allow?

script-src https://croombit.com for the banner and connect-src https://api.croombit.com for its calls.

Does it work with Inertia or Livewire?

Yes. The tag lives in the Blade layout that wraps them, so it loads once, before your app.