HTTP reference

AI API

AI HTTP routes live in routes/ai.php: one token-authenticated webhook (no session) and an authenticated /ai/* group gated by can-use-ai.

Config: AI configuration. Product behaviour: AI assistant.

Webhook (token auth)

MethodPathRoute nameMiddleware
POST/ai/webhooks/collection-importai.webhooks.collection-importthrottle:30,1

CSRF is disabled for this path in bootstrap/app.php.

Authentication

Controller: CollectionImportWebhookController.

  1. Read configured secret: config('ai.webhook_token')AI_WEBHOOK_TOKEN.
  2. Read provided token from either:
    • Authorization: Bearer {token}, or
    • header X-AI-Webhook-Token
  3. Abort 403 if the configured token is empty or hash_equals fails.

Body (validated)

Expects a collection id, associative records array, and optional upsert_key. On success returns JSON with ok, collection_id, url, and import summary fields. Validation failures / import errors return 4xx JSON.

No user context

The webhook has no interactive user; AI mutation logging is skipped for this path. Protect the token like a production secret.

POST /ai/webhooks/collection-import HTTP/1.1
Host: app.example.com
Authorization: Bearer your-AI_WEBHOOK_TOKEN
Content-Type: application/json

Authenticated group

Middleware stack for all routes below:

auth + verified + permission:can-use-ai

Prefix: /ai, name prefix: ai..

Status and shell

MethodPathRoute nameNotes
GET/aiai.indexInertia AI page shell
GET/ai/statusai.statusProvider / readiness JSON (AiStatusController)

Conversations list / create / show / destroy / pin / truncate / bulk-destroy

MethodPathRoute namePurpose
GET/ai/conversationsai.conversations.indexList conversations
POST/ai/conversationsai.conversations.storeCreate conversation
POST/ai/conversations/bulk-destroyai.conversations.bulk-destroyDelete many
GET/ai/conversations/{conversation}ai.conversations.showShow conversation + messages
DELETE/ai/conversations/{conversation}ai.conversations.destroyDelete one
POST/ai/conversations/{conversation}/pinai.conversations.pinToggle pin
POST/ai/conversations/{conversation}/truncateai.conversations.truncateTruncate message history

Persistence tables: agent_conversations, agent_conversation_messages (AI configuration).

Controller: AiConversationController.

Attachments

MethodPathRoute nameThrottle
POST/ai/attachmentsai.attachments.storethrottle:30,1

Uploads chat attachments (CSV/TXT/XLSX/PDF per assistant instructions). Expired files are cleaned by ai:cleanup-attachments.

Controller: AiChatAttachmentController.

Chat

MethodPathRoute nameThrottle
POST/ai/chatai.chatthrottle:30,1

Invokes AppAssistant with tools filtered by the user’s effective permissions. Optional daily cap: AI_DAILY_PROMPT_LIMIT (0 = unlimited).

Controller: AiChatController.

Import job status

MethodPathRoute nameExtra permission
GET/ai/import-jobs/{jobId}ai.import-jobs.showcan-create-collections

jobId must be a UUID. Used to poll async collection import jobs started from AI tools.

Controller: ImportJobStatusController.

Registered last so static /ai/* paths are not captured:

MethodPathRoute nameConstraint
GET/ai/{conversation}ai.show{conversation} UUID

Inertia page for opening a specific conversation (AiPageController@show).

Throttles summary

EndpointLimit
Webhook import30 requests / minute
Attachments store30 / minute
Chat30 / minute

Other authenticated AI routes rely on session auth + Fortify login throttles unless additional limiters are added.

Tool permissions (not HTTP middleware)

Even with can-use-ai, individual tools still call ChecksAiPermissions::requirePermission() for domain actions (files, collections, users, …). Users without matching effective permissions get English machine-error strings from tools (the model should translate them into the user’s language) instead of silent success. See Extending Externa.

Previous
GraphQL