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 SeamwardManager with request or job scope;
  • registers the seamward.webhook middleware 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:

KeyDefaultPurpose
connection_keyrequiredPublic Connection key for one Integration
ingest_tokenrequiredSecret, collector credential
endpointSeamward ingest APIHTTPS delivery endpoint
redaction_policy_versionshape-only-v1Audit label for local shape extraction
hash_keylocally derivedYour own keyed-hash key; see The hash key
hash_namespacelocally derivedOptional hash generation label
drop_fields, hash_fields[]Field names never described in a value profile
value_profilestructuraloff, structural, or full; see Value profiles
value_profile_exclude_fields[]With full: field names whose values are never hashed
deploymentconfig environmentservice, release, and commitSha
max_queue_size1000Bounded process-local queue
max_batch_size50Envelopes per request
timeout_seconds2Connect 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): Collector returns and reuses the named core collector.
  • observeHttp(string $connection, string $method, string $routeTemplate, callable $callback, string $payloadLocation = 'response', mixed $requestPayload = null): mixed records 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 = []): mixed observes one outgoing call made through any client on a named connection and returns the callback's result. It delegates to the core observeCall, records Illuminate\Http\Client\ConnectionException as no response, and runs your extractError before that default.
  • observeQueuePublish(...) and observeQueueConsumer(...) delegate explicit broker callbacks to the named core collector.
  • observeScheduledFeed(...) observes one explicit scheduled import or export callback.
  • record(Observation $observation, ?string $connection = null): void records custom core observations.
  • flush(?string $connection = null): void flushes one named connection or all resolved collectors.
  • stats(string $connection): array returns 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.

Next steps