Laravel collector API
seamward/laravel-collector adapts the framework-neutral PHP collector to Laravel 12 and 13. It requires PHP 8.3 or later and is available from Packagist as an alpha package.
Service provider
Laravel package discovery loads SeamwardServiceProvider. The provider:
- merges and publishes
config/seamward.php; - binds
SeamwardManagerwith request or job scope; - registers the
seamward.webhookmiddleware alias; - registers
seamward:doctor; - flushes resolved collectors after console commands, queue jobs, and application termination.
Named connections
seamward.default selects the default connection. Each entry under seamward.connections accepts:
| Key | Default | Purpose |
|---|---|---|
connection_key | required | Public Connection key for one Integration |
ingest_token | required | Secret, collector credential |
endpoint | Seamward ingest API | HTTPS delivery endpoint |
redaction_policy_version | shape-only-v1 | Audit label for local shape extraction |
hash_key | locally derived | Your own keyed-hash key; see The hash key |
hash_namespace | locally derived | Optional hash generation label |
drop_fields, hash_fields | [] | Field names never described in a value profile |
value_profile | structural | off, structural, or full; see Value profiles |
value_profile_exclude_fields | [] | With full: field names whose values are never hashed |
deployment | config environment | service, release, and commitSha |
max_queue_size | 1000 | Bounded process-local queue |
max_batch_size | 50 | Envelopes per request |
timeout_seconds | 2 | Connect and request timeout |
Use a separate named connection for every Integration direction and protocol boundary.
One ingest token for every Integration
Set the workspace ingest token once as seamward.ingest_token. A connection can then be the Integration's connection name alone, or an array with service_connection and any of the settings above. See One token for every Integration.
Code example
// config/seamward.php'ingest_token' => env('SEAMWARD_INGEST_TOKEN'),'connections' => [ 'paystack_webhook' => 'paystack-webhook', 'crawler_api' => ['service_connection' => 'crawler-api', 'max_batch_size' => 10],],The array key is the name your code passes to Seamward::connection(). The value is the connection name shown on the Integration's Collector setup tab. The token decides the mode, so the same configuration works in Test and Live. A connection that defines connection_key keeps using its own credentials. php artisan seamward:doctor reports a token-based connection as configured (service token, <name>).
SeamwardManager
connection(?string $name = null): Collectorreturns and reuses the named core collector.observeHttp(string $connection, string $method, string $routeTemplate, callable $callback, string $payloadLocation = 'response', mixed $requestPayload = null): mixedrecords request-side or response-side structure while preserving the response or exception. The optional parameters are appended for compatibility.observeCall(string $connection, CallOperation $operation, callable $call, ?string $reference = null, mixed $requestPayload = null, int $attempt = 1, array $correlation = []): mixedobserves one outgoing call made through any client on a named connection and returns the callback's result. It delegates to the coreobserveCall, recordsIlluminate\Http\Client\ConnectionExceptionas no response, and runs yourextractErrorbefore that default.observeQueuePublish(...)andobserveQueueConsumer(...)delegate explicit broker callbacks to the named core collector.observeScheduledFeed(...)observes one explicit scheduled import or export callback.record(Observation $observation, ?string $connection = null): voidrecords custom core observations.flush(?string $connection = null): voidflushes one named connection or all resolved collectors.stats(string $connection): arrayreturns delivery and build counters for one named connection.
All collector build and delivery failures are suppressed. The callback's own exception is recorded and rethrown unchanged.
The queue and feed methods preserve callback results and exact host exceptions. They never acknowledge a message, apply a retry, control a dead-letter queue, or schedule a job. Laravel job events only trigger flushing.
Outcome amount and state
SeamwardManager::record() passes an observation's outcome to the core collector unchanged, so amount and state are reported exactly as the PHP collector reference describes.
Account reference
SeamwardManager::observeCall() accepts accountRef: and record() accepts 'accountRef' in an outcome, exactly as the PHP collector reference describes.
Deprecation notices
SeamwardManager::observeCall() reads the provider's Deprecation, Sunset and API version headers from a Laravel HTTP client response, and from the response on a thrown RequestException, exactly as the PHP collector reference describes. Nothing needs configuring.
Rate limits and signature results
SeamwardManager::observeCall() reads a provider's rate-limit headers from a Laravel HTTP client response, exactly as the PHP collector reference describes.
The ObserveWebhook middleware reports the result of your own signature check when your route records it on the request:
Code example
use Seamward\LaravelCollector\Middleware\ObserveWebhook;
Route::post('/webhooks/payments', function (Request $request) { $valid = $verifier->verify($request->getContent(), $request->header('Provider-Signature')); $request->attributes->set(ObserveWebhook::SIGNATURE_VALID, $valid); if (! $valid) { return response()->json([], 401); } // handle the event return response()->json([], 202);})->middleware('seamward.webhook:payments');Set a bool. The signature and the secret are never read by the middleware.
ObserveWebhook middleware
ObserveWebhook accepts the connection name and stable route template as middleware parameters. It records the request's JSON structure after the handler, keeps the handler response authoritative, and flushes during terminate().
Code example
Route::post('/webhooks/candidates', CandidateWebhookController::class) ->middleware('seamward.webhook:candidate_webhook,/webhooks/candidates');Non-JSON bodies produce a null payload shape. Headers and raw body values are never serialized into the envelope.
The template is optional. When it is omitted, the middleware uses the matched route's uri(), including group prefixes, strips the optional-parameter marker ({event?} becomes {event}), and is unaffected by route model binding. An explicit template still takes precedence, and a request that matched no route is not recorded, because its concrete path might contain customer identifiers. Collector versions before 0.1.0-alpha.2 record nothing for a route that omits the template.
Diagnostics
php artisan seamward:doctor lists connection names as configured or incomplete. It checks presence only and never prints Connection keys, ingest tokens, hash keys, or their values. It also warns when a connection sets value_profile to full without a hash_key; that connection observes at structural until the key is set.
