Looking to hire Laravel developers? Try LaraJobs

laravel maintained by clickfix

Description
ClickFix for Laravel 10, 11, 12 and 13: service provider, ClickFix facade, artisan commands, signed webhook route with one Laravel event per ClickFix event, and the @clickfixWidget Blade directive.
Last update
2026/11/11 12:30 (dev-main)
License
Downloads
5

Comments
comments powered by Disqus

clickfix/laravel

ClickFix for Laravel 10, 11, 12 and 13 (PHP 8.1+): one package that wires clickfix/sdk into a Laravel app. Service provider and ClickFix facade, config/clickfix.php, artisan commands, a signed POST clickfix/webhook route that raises a Laravel event per ClickFix event, and the @clickfixWidget Blade directive that boots the widget with a verified identity (user_hash).

MIT licensed. Developed in the sdks/laravel directory of the ClickFix monorepo and published through the read-only repository ClickSolveMainTR/clickfix-laravel, which Packagist watches as clickfix/laravel: every release pushes this directory there and tags vX.Y.Z, where X.Y.Z is the newest ## X.Y.Z heading in CHANGELOG.md. Fixes land in the monorepo and ship with the next release. Full walkthrough with every other platform: Integration guide.

Install

composer require clickfix/laravel
php artisan clickfix:install

clickfix:install publishes config/clickfix.php, adds the CLICKFIX_* keys to .env (and empty ones to .env.example), generates a webhook secret and prints the webhook URL to subscribe. Fill the keys and run php artisan clickfix:check.

.env key Where it comes from
CLICKFIX_API_KEY Organization → API and automations → API keys (cf_sk_…)
CLICKFIX_BASE_URL https://api.getclickfix.com (no /v1)
CLICKFIX_WIDGET_ID Web widget → Install
CLICKFIX_IDENTITY_SECRET Web widget → Security → Identity verification; stays on the server
CLICKFIX_WEBHOOK_SECRET data.secret of POST /v1/public-api/webhooks, or the generated value used as the subscription secret
CLICKFIX_WEBHOOK_PATH route path, default clickfix/webhook
CLICKFIX_WEBHOOK_QUEUE unset: events fire inside the request; true or a queue name: a queued job fires them
CLICKFIX_TIMEOUT HTTP timeout in seconds for the package's own calls

Package discovery registers the provider and the ClickFix alias; nothing to add to config/app.php or bootstrap/providers.php.

Widget and identity

    @clickfixWidget
</body>

Renders the queue snippet, ClickFix("boot", {...}) and the async widget.js tag. Without arguments the authenticated user is identified (user_id = model key, user_hash computed with the identity secret, name and email from the model); nobody logged in means an anonymous widget. Variants:

@clickfixWidget($customer)                         {{-- a specific model --}}
@clickfixWidget(null)                              {{-- always anonymous --}}
@clickfixWidget(auth()->user(), ['locale' => 'tr', 'theme' => 'dark'])

The fields sent next to user_id come from clickfix.widget.user (attribute name or dotted path), or the model implements ClickFix\Laravel\Contracts\ProvidesClickFixIdentity and returns the identity itself (user_id, name, email, phone, company, attributes). Default boot options for every page live in clickfix.widget.options.

Inertia, Livewire or a SPA that boots the widget itself:

use ClickFix\Laravel\Facades\ClickFix;

ClickFix::userHash($user);   // hex HMAC-SHA256 of the model key
ClickFix::identity($user);   // ['user_id' => '42', 'user_hash' => '…', 'name' => …, 'email' => …]

Share ClickFix::identity($user) with the page and call ClickFix("identify", identity) in the browser. The identity secret never leaves the server.

Webhooks

The provider registers POST /clickfix/webhook (outside the web group: no session, no CSRF) behind the clickfix.webhook middleware, which verifies ClickFix-Webhook-Timestamp and ClickFix-Webhook-Signature: v1=… over the raw body (HMAC-SHA256, 5 minute tolerance by default, constant-time comparison). A bad or stale signature is 401, a missing secret 503. Subscribe the URL clickfix:install prints (public https) with POST /v1/public-api/webhooks or in the panel.

Every verified delivery raises ClickFix\Laravel\Events\WebhookReceived and, for a known event name, the matching class: ConversationCreated, ConversationResolved, ConversationAssigned, MessageReceived, MessageSent, MessageFailed, ContactCreated, ContactUpdated, ContactMerged, CompanyCreated, CompanyUpdated, SlaWarning, SlaBreached, CsatSubmitted, … (one per event in the webhook reference; Webhooks\EventMap::EVENTS). Unknown names raise only WebhookReceived, so new ClickFix events never break the endpoint.

use ClickFix\Laravel\Events\MessageReceived;

Event::listen(MessageReceived::class, function (MessageReceived $event) {
    $event->get('conversation_uuid');   // data_get on the payload's data
    $event->get('contact_external_id'); // your user id
    $event->payload->deliveryId;        // ClickFix-Webhook-Id
    $event->payload->occurredAt();      // Carbon
});

Retries carry the same ClickFix-Webhook-Id; the controller remembers it in the cache for clickfix.webhook.dedupe_ttl seconds (default one day) and acknowledges a repeat without firing the events again. Set CLICKFIX_WEBHOOK_QUEUE=true (or a queue name, plus CLICKFIX_WEBHOOK_QUEUE_CONNECTION) to fire the events from the ProcessWebhook job instead of inside the request; the endpoint then answers 202. To register the route yourself set CLICKFIX_WEBHOOK_ROUTE=false and attach the clickfix.webhook middleware to your own route.

Artisan

Command What it does
clickfix:install [--force] [--no-env] Publish config, add .env keys, generate the webhook secret, print the webhook URL and next steps
clickfix:check Verify the API key against the API (GET /v1/public-api/contacts?limit=1; 403 still proves the key), widget id, identity secret, webhook secret length and the webhook route (https). Exit code 1 on any failure
clickfix:user-hash {user} [--raw] [--json] user_hash for a model key (or any user_id with --raw), to debug WIDGET_IDENTITY_INVALID
clickfix:webhook:secret [--write] 64 hex character secret; --write sets CLICKFIX_WEBHOOK_SECRET in .env
clickfix:sync:contacts [--id=*] [--since=] [--chunk=] [--dry-run] Upsert the configured model into ClickFix contacts by external id (PUT /v1/public-api/contacts/by-external-id, scope contacts:write)

clickfix:sync:contacts reads clickfix.contacts: the Eloquent model (default App\Models\User), a fields map (name, email, phone, job_title, locale, timezone → attribute or dotted path) and custom_fields (ClickFix custom field key → attribute). external_id is always the model key, the same value identify sends, so a synced contact and the widget session are one person.

API client

ClickFix::client() returns the clickfix/sdk client configured from config/clickfix.php for calls from your own code. ClickFix::http() is a Laravel HTTP client bound to the API (bearer key, JSON, timeout, retries on 429/5xx); the commands use it so Http::fake() covers them in your tests.

Tests

composer install
vendor/bin/phpunit

CI runs the suite with orchestra/testbench on Laravel 10 (PHP 8.1–8.3), 11 and 12 (PHP 8.2–8.4) and 13 (PHP 8.3–8.4).