escalated-dev/escalated-laravel
A full-featured, embeddable support ticket system for Laravel with SLA tracking, escalation rules, agent workflows, and a customer portal. Self-host or sync to cloud with one config change.
embed.js script tag plus an allowlist of origins. This turns the decision from "do we add a support page" into "where do we want support to appear across our whole web estate," including properties that are not Laravel./support/widget plus public KB search turn support into a product surface (deflection, self-service, guest tickets via magic links) rather than a cost center. Useful input for pricing/packaging decisions — e.g., gating SLA and branded email behind a paid tier.metadata (order number, plan, page URL) as a sanitised flat map, alongside a server-recorded embedded_on origin. That is the difference between "a stranger typed something" and a support conversation with the customer's plan, order, and journey attached — a decision input for deflection quality, routing rules, and any support-to-revenue analytics you plan to build.self-hosted mode keeps all ticket PII in your own database with no external calls. That is a decision input for regulated or EU customers, and for security reviews that reject third-party subprocessors. Note the new cross-origin path is a deliberate, scoped relaxation (see below) rather than a new outbound dependency.embed.js route slightly softens this: the widget can now live on a non-Inertia site without that site adopting the frontend stack.Ticketable/TicketSubject contracts let you model support against your own domain objects. This shapes how deeply support is woven into the product versus bolted on.Adopt when:
embed.js covers this.user_key_type override).Look elsewhere when:
escalated.dev as a dependency you cannot easily replace.Specific to v1.9.4:
escalated.widget.serve_script to disable the script route if you don't embed externally.Mitigations if adopting: pilot on one product surface, pin the version, keep the migrations under your own review, and confirm an exit path — the data lives in your database, so extraction is feasible even if the package is abandoned.
To executives: This is an MIT-licensed helpdesk we install into our Laravel app instead of renting one. It covers the capabilities customers actually evaluate — SLA tracking, escalation rules, an agent queue, a customer portal, a knowledge base, and an embedded support widget — and it ships in days rather than a quarter of engineering time. The latest release makes that widget embeddable on any of our sites with a single script tag, so support can appear on the marketing site, docs, and other properties without per-site integration work, and each conversation can carry context like plan or order number. Because it runs self-hosted by default, customer ticket data stays in our database, which simplifies security reviews and data-residency questions, and we remove per-agent SaaS fees from the support budget. The tradeoff is community size: the project has limited public adoption, so we should run it as a scoped pilot, monitor it, and retain a documented exit path. There is also a paid cloud tier available if we later want a unified inbox across multiple apps — a possible upsell path rather than a requirement.
To engineering: Laravel-native, Composer-installed, artisan escalated:install plus migrations, with a Vue 3 component library pulled from npm and wired through a page resolver. Authorization is via two Gates (escalated-admin, escalated-agent); host models opt in through Ticketable/HasTickets and optional TicketSubject implementations. User key types (integer, UUID, ULID, string) are auto-detected at migration time. Broadcasting is opt-in (Pusher/Reverb/Soketi) with polling fallback, email threading headers are handled, and code style is enforced with Pint in CI.
v1.9.4 adds a cross-origin embedding path: list origins in escalated.widget.allowed_origins (ESCALATED_WIDGET_ALLOWED_ORIGINS) or the admin widget_allowed_origins setting, with https://*.example.com wildcard support; listed origins get CORS headers (including on 422 and 429), bypass CSRF, and are forced to guest. GET {prefix}/widget/embed.js serves a standalone bundle from @escalated-dev/escalated, disableable via escalated.widget.serve_script. Widget tickets and chats now persist host metadata, sanitised to a flat map of short scalars, plus a server-set embedded_on origin; /config advertises accepts_metadata: true. Also fixed: admin settings now actually save widget_enabled, colour, position, greeting, and departments, and live-chat metadata is bounded like ticket metadata. The binding constraints remain the Inertia/Vue agent UI and the migration-time schema decisions — review both before we commit, and route the CSRF-exempt guest path through a security review.
How can I help you explore Laravel packages today?