Build A Support-Ticket Classifier With Laravel Ai Sdk 1.0
Published on October 4, 2026 by Dinesh Uprety
Build a Support-Ticket Classifier with Laravel AI SDK 1.0
Every SaaS has the same Monday-morning problem: a pile of support tickets, and nobody knows which ones are urgent, which team should take them, or which are just spam. Someone has to read each one and route it by hand.
Laravel AI SDK 1.0 ships a feature built exactly for this: Classification. Instead of asking an LLM to write an essay about your ticket, you ask it small, sharp questions — "Is this urgent? Yes or no." — and get back typed answers your code can act on immediately. It is fast, cheap, and boring in the best possible way.
In this tutorial we will build a ticket classifier end to end: install the SDK, classify tickets into departments, detect urgency, filter spam with one line, and gate a dangerous tool (refunds) behind human approval.
What you need
- A Laravel 12/13 app (PHP 8.3+)
- A TypeSafe or OpenRouter API key — classification runs on fast, cheap classification models, not full LLMs
1. Install the SDK
composer require laravel/ai
Publish the config and drop your provider key into .env:
php artisan vendor:publish --tag=ai-config
TYPESAFE_API_KEY=your-key-here
That's the whole setup. One API, any provider — the SDK handles the plumbing.
2. The Ticket model
Assume a simple tickets table with subject, body, department, is_urgent, and priority_score columns. The classifier fills in the last three.
3. Ask the AI three questions at once
This is the heart of it. The Classification capability takes your text and a set of questions, and returns a typed answer for each:
use Laravel\Ai\Classification;use Laravel\Ai\Classification\Boolean;use Laravel\Ai\Classification\Choice;use Laravel\Ai\Classification\Score; $response = Classification::of($ticket->body)->questions([ 'is_urgent' => new Boolean('Does this message convey urgency?'), 'department' => new Choice('Which team should handle this?', [ 'billing' => 'Payments, invoicing, refunds', 'technical' => 'Bugs, outages, integrations', 'sales' => 'Pricing, plans, upgrades', ]), 'priority' => new Score('How critical is this issue?'),])->classify(); $ticket->update([ 'is_urgent' => $response['is_urgent']->isTrue(), 'department' => $response['department']->choice, 'priority_score' => $response['priority']->score, // 0.0 - 1.0]);
Three things to notice:
- Typed answers, not prose.
Booleangives youisTrue(),Choicegives you one of your keys,Scoregives you a float between 0 and 1. No parsing, no regex on model output. - You describe each choice. The descriptions ("Payments, invoicing, refunds") are what the model actually reads to decide. Write them like you'd explain the team to a new hire.
- It runs on classification models (TypeSafe's Jev), which answer in milliseconds at a fraction of LLM cost. This is not "call GPT-4 for every ticket."
4. Classify automatically when a ticket arrives
Hook it into a model observer or a queued job so routing happens in the background:
class ClassifyTicket implements ShouldQueue{ public function handle(): void { // ... classification from step 3 ... // Notify the right team Notification::route('slack', config('teams.'.$ticket->department)) ->notify(new TicketRouted($ticket)); }}
New ticket comes in → job classifies it → the right team's Slack channel lights up. Nobody reads a queue by hand anymore.
5. One-line spam filter with Str::decide
For a single yes-or-no question, you don't even need the full API. The new Str::decide macro returns a boolean:
use Illuminate\Support\Str; if (Str::of($ticket->body)->decide('Is this spam?')) { $ticket->markAsSpam(); return;}
There is also a threshold argument that sets how certain the model must be before it says yes — handy when false positives are expensive.
6. Gate dangerous tools behind human approval
Classification routes tickets. But what about acting on them — say, issuing a refund? You don't want an agent doing that unsupervised.
In 1.0, a tool that implements the Approvable contract pauses the agent until a human approves:
use Laravel\Ai\Concerns\InteractsWithApprovals;use Laravel\Ai\Contracts\Approvable;use Laravel\Ai\Contracts\Tool; class IssueRefund implements Approvable, Tool{ use InteractsWithApprovals; // ...}
When the agent wants to call it, the run pauses and your app gets the pending call with the exact arguments the model chose. You can then approve it, reject it with a reason the model sees, or edit the arguments before it runs. Approvals work with prompt, stream, queue, and broadcast methods.
The pattern this unlocks is powerful: the agent drafts the action, a human (or your business rules) approves it. Automation with a seatbelt.
7. What else is new in 1.0
A few more things worth knowing:
- Per-step middleware — agent middleware now runs on every generation step, so you can swap models or drop expensive tools mid-conversation.
- Tool search — wrap rarely used tools in
ToolSearchand the provider only loads them when a prompt needs them. - Code execution — run code in the provider's sandbox on Anthropic, OpenAI, Azure, Gemini, and xAI.
- Conversation storage — messages now use a single
stepsJSON column. If you querytool_calls/tool_resultswith raw SQL, migrate tosteps(the upgrade guide includes a backfill migration — run it once before deploying).
Wrapping up
Classification is one of those features that sounds small and changes a lot. Typed yes/no/multiple-choice answers turn "AI" from a chatbot demo into a routing layer you can trust in production: tickets sorted, spam filtered, urgency flagged, and dangerous actions parked behind approval.
Try it on your own inbox first — point Classification::of() at a few real tickets and see how it sorts them. You might be surprised how rarely it gets it wrong.
Senior Software Engineer • Writer @ Laranepal • PHP, Laravel, Livewire, TailwindCSS & VueJS • CEO @ Laranepal & Founder @ laracodesnap