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.