Integration Logs
A SaaS back-office needs answers when an integration misbehaves: what did we send that webhook endpoint, what did it reply, and what is that API token actually calling? Kinetix ships both halves — an API request logger (middleware + store) and a unified viewer component that also surfaces the webhook delivery log.


1. Webhook delivery logs
The Webhooks module already records every delivery attempt (event, payload, response, status, attempt) in kinetix_webhook_logs. Two feeds expose it, both gated by webhooks.manage and scoped to the active team's endpoints:
| Method | Endpoint | Description |
|---|---|---|
GET | {prefix}/webhooks/logs | All endpoints — ?result=success|failed, ?search= (event / endpoint name) |
GET | {prefix}/webhooks/{endpoint}/logs | One endpoint's deliveries |
POST | {prefix}/webhooks/logs/{log}/redeliver | Re-dispatch a delivery |
Each entry includes the payload, the response body, and the endpoint's name/URL — everything the detail modal shows. Logging is automatic with the module (no extra config); tune it with kinetix.webhooks.log_payloads (default true — disable for sensitive payloads) and kinetix.webhooks.response_limit (stored response cap, default 1000 chars). Retention: kinetix:webhooks:prune (config kinetix.webhooks.retention_days).
2. API request logs
Opt-in module for logging requests to your token-authenticated API:
// config/kinetix.php
'api_logs' => [
'enabled' => true,
'log_request_body' => false, // opt-in, redacted + size-capped
'log_response_body' => false,
'body_limit' => 10240, // bytes
'retention_days' => 30,
'redact' => ['password', 'secret', 'token', /* … */],
],php artisan vendor:publish --tag=kinetix-api-logs-migrations
php artisan migrateAttach the middleware to your API group — the row is written in terminate() (after the response is sent), so logging adds no request latency:
Route::middleware(['auth:sanctum', 'kinetix.api-log'])
->prefix('api/v1')
->group(function () { /* … */ });Each row records the method, path, status, duration (ms), ip, the Sanctum token id/name (via currentAccessToken()), and — only when enabled — the request body (sensitive keys replaced with [redacted], oversized payloads stored as a truncation marker) and the response body (truncated at body_limit).
The feed (GET {prefix}/api-logs, filters ?result= / ?search=) is gated by viewKinetixApiLogs — local-only by default, so define the gate in production:
Gate::define('viewKinetixApiLogs', fn ($user) => $user->isAdmin());Keep the table bounded — schedule the prune:
Schedule::command('kinetix:api-logs:prune')->daily();Multi-tenant
With kinetix.teams on, each row is attributed to the caller's team and the feed is scoped strictly — logs carry paths, token names and optionally bodies, so there is no shared pool here: unlike mail templates, a NULLteam_id means unattributed, not visible to everyone.
The tenant is resolved from a team segment when your API route has one (api/v1/{current_team}/…), otherwise from the token holder'scurrentTeam — which is what a typical token-authenticated route resolves to, since it has no session and no segment.
php artisan vendor:publish --tag=kinetix-api-logs-migrations --force
php artisan migrateAdditive and idempotent. Rows written before the migration keep team_id NULL and therefore stop appearing inside a team's viewer — deliberately, since there is no way to know which tenant they belonged to. They age out with the retention prune. Single-tenant apps are unaffected: Kinetix omits the column entirely when the module isn't team-scoped, and the scope is a no-op.
3. The viewer component
<script setup lang="ts">
import KinetixIntegrationLogs from '@/components/kinetix/KinetixIntegrationLogs.vue';
</script>
<template>
<!-- Both feeds, tabbed -->
<KinetixIntegrationLogs />
<!-- Or a single feed -->
<KinetixIntegrationLogs only="api" />
</template>- Tabs: Webhook deliveries · API requests (hide one with
only). - Filters: success/failed band + debounced search; paginated (15/page).
- Detail modal: pretty-printed payload/request body, the response, status, attempt/duration — and one-click redeliver for webhook entries.
Each feed enforces its own gate server-side (webhooks.manage / viewKinetixApiLogs); mount the component behind the same check for a clean denied state.

