Roles & Tools
Agent roles define what an AI agent can do — its personality (system prompt), which tools it can call, how creative it should be (temperature), and optionally which LLM provider to use.
Default Roles
Three roles ship out of the box:
Support
For customer-facing inquiries — order status, tracking, returns, general questions.
{
"name": "Customer Support",
"system_prompt": "You are a customer support agent for {{tenant_name}}...",
"allowed_tools": ["orders.get", "orders.getStats", "customers.*"],
"temperature": 0.3,
"max_tokens": 1024
}
Low temperature (0.3) for consistent, factual responses.
Operations
For internal operations — order management, fulfillment, shipping.
{
"name": "Operations Assistant",
"allowed_tools": ["orders.*"],
"temperature": 0.1,
"max_tokens": 2048
}
Very low temperature (0.1) for precise, action-oriented responses.
Analytics
For business questions — revenue trends, order volumes, product performance.
{
"name": "Analytics Assistant",
"allowed_tools": ["orders.getStats"],
"temperature": 0.2,
"max_tokens": 2048
}
API Endpoints
List Roles
GET /api/v1/ai/agent/roles
{
"data": [
{"id": "support", "name": "Customer Support", "allowed_tools": ["orders.get", "customers.*"], ...},
{"id": "operations", "name": "Operations Assistant", ...},
{"id": "analytics", "name": "Analytics Assistant", ...}
],
"default_role": "support"
}
Create or Update a Role
PUT /api/v1/ai/agent/roles/custom-role
{
"name": "Shipping Specialist",
"description": "Handles shipping, tracking, and delivery questions",
"system_prompt": "You are a shipping specialist for {{tenant_name}}. You help with tracking, delivery estimates, and shipping issues. Always include tracking URLs when available.",
"allowed_tools": ["orders.get", "orders.getStats"],
"temperature": 0.2,
"max_tokens": 1024,
"provider_id": null
}
Set Default Role
PUT /api/v1/ai/agent/roles/default
{
"role_id": "support"
}
Delete a Custom Role
DELETE /api/v1/ai/agent/roles/custom-role
Default roles (support, operations, analytics) cannot be deleted — only customized.
API Permission Scoping
All agent endpoints require the ai.view or ai.configure tenant permission:
| Endpoint | Permission Required |
|---|---|
POST /ai/agent/chat |
ai.view |
POST /ai/agent/chat/stream |
ai.view |
GET /ai/agent/roles |
ai.view |
GET /ai/agent/tools |
ai.view |
PUT /ai/agent/roles/{id} |
ai.configure |
DELETE /ai/agent/roles/{id} |
ai.configure |
POST /ai/agent/graph/run |
ai.configure |
Users without ai.view cannot access the AI widget, voice assistant, or any agent endpoints. The widget and voice assistant are conditionally rendered based on these permissions.
Tool Permissions
How Tools Are Discovered
Tools come from module bus endpoints declared in each module's module.json:
// modules/Orders/module.json → api.provides
{
"orders.get": { "description": "Get a single order by ID" },
"orders.create": { "description": "Create a new order via ingest pipeline" },
"orders.updateStatus": { "description": "Update order status" }
}
When building the TenantContext, Laravel reads all bus endpoints from installed modules via ModuleApiRegistry::getAllProvided() and filters them by the role's allowed_tools patterns.
Modules with Tools
| Module | Tools | Examples |
|---|---|---|
| Orders | 10 | orders.get, orders.create, orders.updateStatus, orders.getStats |
| ResellerAdmin | 8 | admin.approveReseller, admin.getNetworkTree, admin.generateReferralCode |
| ChannelLiveChatWidget | 6 | chat.acceptChat, chat.getConversationHistory, chat.transferToHuman |
| Communications | 5 | comms.send, comms.sendBulk, comms.getDeliveryStatus |
| ResellerCatalog | 4 | catalog.calculatePrice, catalog.setPricing, catalog.getAvailableCatalog |
| ResellerOrders | 4 | orders.createChainOrder, orders.forwardOrder, orders.markShipped |
| ResellerNetwork | 3 | network.getDashboard, network.getDownlineTree |
| ResellerFinance | 2 | finance.processDeliveredOrder, finance.getSettlementSummary |
To add tools to other modules, see Adding Tools to Modules.
List Available Tools
GET /api/v1/ai/agent/tools
{
"data": [
{"name": "orders.get", "module": "Orders", "description": "Get a single order by ID"},
{"name": "orders.create", "module": "Orders", "description": "Create a new order via ingest pipeline"},
{"name": "orders.updateStatus", "module": "Orders", "description": "Update order status"},
{"name": "orders.getStats", "module": "Orders", "description": "Get order statistics"}
]
}
Wildcard Patterns
Tool permissions support wildcards:
| Pattern | Matches |
|---|---|
orders.get |
Only orders.get |
orders.* |
orders.get, orders.create, orders.updateStatus, etc. |
* |
All tools from all modules |
Pattern matching uses prefix comparison — orders.* matches any tool starting with orders..
Two-Level Permission Enforcement
Tools are filtered at two independent levels before the AI agent can use them:
Level 1 — Role filtering (Laravel):
The role's allowed_tools patterns filter which tools are sent to the Python agent. AgentGatewayService::buildToolList() iterates all registered tools and matches against the patterns. Tools not matching any pattern are excluded from the TenantContext.
Level 2 — User RBAC (Python agent):
The Python agent infers what tenant permission each tool requires from its name:
| Tool Name Pattern | Inferred Permission |
|---|---|
*.get*, *.search*, *.Stats* |
{module}.view |
*.create* |
{module}.create |
*.update*, *.process*, *.reserve* |
{module}.manage |
*.delete*, *.cancel* |
{module}.delete |
| Other | {module}.view (default) |
Examples:
orders.get→ requiresorders.vieworders.create→ requiresorders.createorders.updateStatus→ requiresorders.manageorders.processRefund→ requiresorders.managecatalog.getAvailableCatalog→ requirescatalog.view
The agent checks each inferred permission against the user's actual tenant permissions (from Spatie). If the user lacks the permission, the tool is silently skipped.
Result: An agent can never do more than the intersection of what the role allows AND what the user's RBAC permits. A support agent with only orders.view can never trigger orders.create even if the role's allowed_tools includes orders.*.
System Prompt Variables
System prompts support the {{tenant_name}} variable, replaced at runtime:
"You are a support agent for {{tenant_name}}."
→ "You are a support agent for Acme Corp."
Provider Overrides
Each role can optionally use a different LLM provider:
{
"name": "Budget Support",
"provider_id": "groq",
"temperature": 0.3,
"allowed_tools": ["orders.get"]
}
If provider_id is null, the tenant's default provider is used.