Usage
Handling Results
You never write webhook-handling code. The SDK ships the route, verifies the signed callback,
and matches it back to your ParseRequest, then fires a Laravel event. All you write is what
to do with a finished document.
And you don't even start from scratch: parse:install
dropped a listener into app/Listeners/HandleParsedDocument.php. Open it and fill in the body:
<?php
namespace App\Listeners;
use ParseForArtisans\Events\ParseCompleted;
class HandleParsedDocument
{
/**
* Create the event listener.
*/
public function __construct() {}
/**
* Handle the event.
*/
public function handle(ParseCompleted $event): void
{
$parse = $event->request; // the ParseRequest, now 'completed'
$markdown = $parse->markdown(); // the parsed Markdown
$document = $parse->parsable; // the model you passed to ->for() (null if none)
// Your logic: store it, index it, send it to an LLM, notify the user…
// $parse->meta and $parse->page_count are here too.
}
}
On Laravel 12 and 13 this listener is auto-discovered: it's live the moment the file
exists, with nothing to register. A companion HandleFailedParse stub is published for errors:
<?php
namespace App\Listeners;
use ParseForArtisans\Events\ParseFailed;
class HandleFailedParse
{
public function handle(ParseFailed $event): void
{
report("Parse failed: {$event->request->error}");
// Your logic: flag the document, alert someone, queue a retry…
}
}
Prefer to wire your own? Skip the publish and write any listener for
ParseCompleted/ParseFailed; they're auto-discovered the same way. The published stubs are just a head start; once published they're yours, so a later SDK update won't overwrite them.
$parse->markdown()reads the result from your bucket (or fetches the managed copy). You can also go straight to the file:Storage::disk($parse->disk)->get($parse->output_path).
Checking status
The Markdown is always delivered by the event. But you'll often want to
show the end user where their document is: "still parsing", "done", or "something went
wrong". That's what ->status() is for.
It reads the local parse_requests row, which the SDK keeps current as results come in (the
webhook updates it in production; the background poll does locally). It's a plain DB read (no
API call), so it's cheap to hit on every page load:
$parse = Parse::find($id); // look up the row by id
$label = match ($parse->status()) {
'pending' => 'Still parsing…',
'completed' => 'Done',
'failed' => 'Something went wrong: ' . $parse->error,
};
Drive a progress badge or a Livewire/polling spinner with it. ->status() only tells you where
the job is; it never fetches the Markdown. The result still arrives through the
ParseCompleted event.
Use it with the Laravel AI SDK
Parsed Markdown drops straight into a prompt:
use function Laravel\Ai\agent;
public function handle(ParseCompleted $event): void
{
$markdown = $event->request->markdown();
$summary = agent()->prompt("Summarize this contract:\n\n{$markdown}")->text;
}
Limits & errors
| Aspect | Your bucket (BYO) | Managed dev bucket |
|---|---|---|
| Max file size | PDFs up to 1 GB; other formats lower | smaller, dev-sized cap |
| Total storage | your own | quota-limited |
| Result retention | yours to keep | ~1 day |
| Best for | production, bulk | local development |
Results are delivered by event (ParseCompleted / ParseFailed): parse-time problems
arrive as a ParseFailed event carrying $request->error, not as a thrown exception. Only
submission problems (bad key, unsupported type, quota exceeded) throw
ParseForArtisans\Exceptions\ParseException from ->parse() itself.
Size and quota numbers above are examples and not yet final.