← Back to the blog

The stack behind There There

Freek
Freek
May 29, 2026 · 6 min read
The stack behind There There

At Spatie, we like to build in the open. We've just invited the first people who applied into the There There beta, so this feels like a good moment to share what it's made of and why we made the choices we did. The stack you pick shapes how fast you can move and how much of your time goes into the actual product instead of the plumbing.

There There is a Laravel app. That surprises nobody who knows us. At Spatie we've been building quality Laravel applications since the framework's early days, so it's the foundation we know best. The interesting parts are the choices around the edges, so let me walk through the whole thing.

Laravel, on purpose

We've shipped Laravel apps for over a decade. We know its sharp edges and its escape hatches, and that accumulated knowledge is worth more to us than the newest framework of the month. Boring, in the sense of predictable, is a feature here. It means we spend our days on support workflows and AI, not on reinventing queues and authentication.

The app runs on the latest PHP and Laravel. Sign-in is handled by Fortify, with Socialite for logging in through Google and GitHub. API access uses Sanctum and Passport, depending on the client.

Inertia and React for the front end

The interface is built with Inertia and React, with TypeScript and Tailwind throughout. Inertia is the part I'd recommend to anyone building a Laravel app with a rich front end. You get the single-page feel without standing up and maintaining a separate API. The server stays the source of truth, and a controller hands a React page its props directly.

We use Wayfinder to generate typed functions for our routes, so the front end calls backend endpoints without us copying URLs around by hand. When a route changes, TypeScript tells us what broke.

A structure organised by domain

Instead of letting app/Models and app/Http grow into a sprawl, we organise the code by domain. Each big concept in the app gets its own folder with everything it needs close by.

app/Domain/
  Tickets/
  Channels/
  Brain/
  Workspace/
  Ai/
  Workflow/

Each domain holds its own models, actions, events, and the rest. Related code lives together, and it's obvious where a new feature belongs. If you want the full reasoning behind this approach, we cover it in depth in our Laravel Beyond CRUD course.

The AI part

The AI in There There runs on Laravel's AI SDK. When a ticket comes in, we gather context: the conversation so far, the customer's history, and the most relevant knowledge base articles. We find those articles with embeddings, so a search matches on meaning rather than on exact words. The model drafts a reply from that context, and it never sends anything to a customer on its own. A person always signs off.

MCP, in both directions

We lean on the Model Context Protocol two ways. There There ships an MCP server, so you can point an AI client at your workspace and have it read and manage tickets through a defined set of tools. Internally, our own agents use those same tools to do their work. The protocol gives the AI an explicit, safe list of actions instead of free rein over the database.

The unglamorous plumbing

A helpdesk lives and dies on boring infrastructure. Background work goes onto queues watched by Horizon. Live updates, like seeing a colleague pick up a ticket while you're looking at it, travel over websockets with Reverb and Echo. And we roll features out gradually with Pennant.

Keeping ourselves honest

Every change is covered by tests written with Pest, and we run Larastan over the codebase to catch type problems before they ship. Pint keeps the formatting consistent so nobody has to argue about it.

Our own packages

A fair amount of There There is held together by packages we wrote at Spatie and have leaned on for years. That isn't loyalty for its own sake. We reach for them because we already trust them in production.

Most of the data that moves between the backend and the React front end is shaped by laravel-data, and laravel-typescript-transformer turns those same objects into TypeScript types so the two sides can't drift apart. Ticket and channel queries are filtered and sorted with laravel-query-builder, and anything that needs a manual order uses eloquent-sortable.

Attachments on tickets and knowledge base articles are handled by laravel-medialibrary. When someone wants a ticket thread as a document, we render it with laravel-pdf, which drives a headless browser through Browsershot. The social share images you see when a page gets posted somewhere are generated on the fly by laravel-og-image.

Filling the knowledge base means reaching out to the wider web, so we fetch pages with our crawler, and we check records with dns when you connect an email channel. On the operations side, laravel-backup keeps the database safe, activitylog records who changed what, laravel-slack-alerts pings us when something noteworthy happens. There's more in the composer file, but you get the idea.

Used services

Beyond the code, a handful of hosted services keep There There running.

Flare is where our errors, logs, and performance data go. When something throws in production, we get the exception with full context, and the performance side shows us which routes and queries are slowing down before anyone files a ticket about it. It's our own product, so we feel every rough edge firsthand.

Oh Dear watches the site from the outside. Uptime, scheduled jobs that quietly stopped running, broken links, expiring certificates, mixed content, and more. When the site goes down or a cron misses its window, we hear about it straight away. That one's ours too.

Inbound and outbound email runs through Postmark. It's what turns an incoming email into a ticket, and a reply back into an email that lands in the customer's inbox.

Payments and subscriptions run on Stripe, wired into the app with Laravel's Cashier.

Hosting runs on UpCloud. These days we prefer infrastructure that's fully owned by European companies, and UpCloud, based in Finland, fits the bill, so that's where There There lives.

Even this blog

The blog you're reading is part of the story. It doesn't live in a database. The posts come from a content API, fetched and cached on demand, with the feed generated by a small package of ours. That let us add a blog in an afternoon instead of bolting a CMS onto the product.

In closing

None of these choices are flashy, and that's the point. A framework we know well, a structure that keeps code where you expect it, and a short list of well-worn tools let us spend our attention on the thing that actually matters: making support feel less like a chore. There There is built by the team at Spatie, the same people behind a long list of open source Laravel packages.

Continue reading

A "Thank You" shouldn't reopen a ticket

A "Thank You" shouldn't reopen a ticket

A closed ticket gets a "thanks" and most helpdesks drag it back open. Here is the layered, fail-safe way There There decides to leave it closed.

Freek
Freek · May 30, 2026 · 4 min read