Tenant API v1 — AI Assistant
Base path: /api/tenant/v1
Middleware: auth:tenant-api, tenant.user, not.suspended, verified, module:ai, plus Spatie permissions noted per route.
Billing: Platform mode burns wallet credits (HTTP 402 when insufficient). BYOK uses the tenant provider key and does not debit the wallet.
Permissions
| Permission | Purpose |
|---|---|
ai.use | Conversations, messages, credits summary, Lead Copilot |
ai.confirm | Confirm or cancel pending write actions |
ai.manage | Update/test workspace AI settings (PUT /settings AI keys, POST /settings/test-ai) |
Credits
GET /ai/credits
Requires ai.use. Runs ensurePeriod() then returns dual-balance wallet summary and recent ledger entries.
{
"included_remaining": 275,
"prepaid_balance": 1000,
"available": 1275,
"period_ym": "2026-08",
"recent_ledger": []
}Conversations
GET /ai/conversations
List current user's conversations (newest first). Requires ai.use.
Query: limit (default 20).
POST /ai/conversations
Create a conversation. Body: optional context object (page/module hints).
GET /ai/conversations/{conversation}
Fetch conversation with messages. Scoped to the authenticated user; foreign workspace IDs → 404.
POST /ai/conversations/{conversation}/messages
Send a user message and receive structured assistant output.
Body:
{
"message": "What should I focus on today?",
"context": { "module": "tasks" }
}Success payload includes:
structured.answer,structured.insights,structured.suggested_actions,structured.referencesusagetoken counts andcredits_burned(platform mode)recent_messages(latest user + assistant turns)
Errors:
| Code | When |
|---|---|
| 402 | Platform wallet empty or below pre-provider credit ceiling (insufficient_ai_credits) |
| 429 | AI route rate limit (throttle:ai) |
| 503 | AI disabled platform-wide or module not entitled |
| 422 | Validation |
Pending write actions
Low-risk writes (for example create_task) create a pending row first.
POST /ai/actions/{action}/confirm
Requires ai.confirm. Executes the tool (also requires underlying domain permission, e.g. tasks.create).
POST /ai/actions/{action}/cancel
Requires ai.confirm. Marks pending action cancelled.
Lead Copilot
Requires ai.use and view policy on the lead.
| Method | Path | Description |
|---|---|---|
| POST | /ai/leads/{lead}/summarize | One-paragraph overview + themes |
| POST | /ai/leads/{lead}/next-action | Recommended next step |
| POST | /ai/leads/{lead}/draft-follow-up | Draft message; body channel: general | email | whatsapp |
Responses mirror conversation message structured output + usage.
Agent tools (server-side)
Tools are not direct HTTP endpoints. The registry exposes them to the agent when module + permission gates pass:
Workspace (available with ai.use; per-module entitlement + view inside providers): search_workspace
Leads (leads.view / leads.update / leads.assign): search_leads, get_lead, get_stale_leads, get_recent_lead_activity, update_lead_status (pending confirmation), assign_lead (pending confirmation; assigned_to is user id or null to unassign), add_lead_note (pending confirmation; text only)
Tasks (tasks.view / tasks.create / tasks.update / tasks.assign / tasks.complete): search_tasks, get_my_tasks, get_overdue_tasks, get_tasks_due_today, get_task, create_task (pending confirmation), update_task_status (pending confirmation; visible with tasks.update or tasks.complete; complete/reopen need tasks.complete at propose+confirm; other statuses need tasks.update), assign_task (pending confirmation), add_task_note (pending confirmation; text only)
Projects (projects.view / projects.update / projects.assign): search_projects, get_project, get_overdue_projects, update_project_status (pending confirmation; requires projects.update; same as HTTP POST …/status), assign_project (pending confirmation), add_project_note (pending confirmation; text only)
Opportunities (opportunities.view / opportunities.update / opportunities.assign): search_opportunities, get_pipeline_summary, get_opportunity_stages, get_opportunity, update_opportunity_stage (pending confirmation; stage_id integer), assign_opportunity (pending confirmation), add_opportunity_note (pending confirmation; text only)
Invoices (invoices.view / invoices.update / invoices.assign / invoices.send / invoices.void): get_overdue_invoices, get_invoice_balance_summary, get_invoice, update_invoice_status (pending confirmation; visible with invoices.update or invoices.send or invoices.void; Unpaid needs send, Cancelled needs void, other targets need update at propose+confirm — same as HTTP POST …/status), assign_invoice (pending confirmation), add_invoice_note (pending confirmation; text only)
Estimates (estimates.view / estimates.update / estimates.assign / estimates.send / estimates.accept): get_estimate, update_estimate_status (pending confirmation; visible with estimates.update or send or accept; Sent needs send, Accepted needs accept, other targets need update at propose+confirm — same as HTTP POST …/status), assign_estimate (pending confirmation), add_estimate_note (pending confirmation; text only)
Quotations (quotations.view / quotations.update / quotations.assign / quotations.send / quotations.accept): get_quotation, update_quotation_status (pending confirmation; visible with quotations.update or send or accept; Sent needs send, Accepted needs accept, other targets need update at propose+confirm — same as HTTP POST …/status), assign_quotation (pending confirmation; EligibleOpportunityAssignee), add_quotation_note (pending confirmation; text only). Quotation payloads expose title (no number field).
Payments (payments.view / payments.update / payments.assign / payments.post / payments.void): get_payment, update_payment_status (pending confirmation; visible with payments.update or post or void; Posted confirm calls post(), Void confirm calls void(); Draft target rejected), assign_payment (pending confirmation), add_payment_note (pending confirmation; text only)
Credit Notes (credit-notes.view / credit-notes.update / credit-notes.assign / credit-notes.issue / credit-notes.apply / credit-notes.void / credit-notes.refund): get_credit_note, update_credit_note_status (pending confirmation; visible with credit-notes.update or issue or apply or void or refund; Issued confirm calls issue(), Applied calls apply(), Void calls void(), Refunded calls refund(); Draft target rejected), assign_credit_note (pending confirmation; EligibleCreditNoteAssignee), add_credit_note_note (pending confirmation; text only)
Purchase Orders (purchase-orders.view / purchase-orders.update / purchase-orders.assign / purchase-orders.send / purchase-orders.receive / purchase-orders.cancel): get_purchase_order, update_purchase_order_status (pending confirmation; visible with purchase-orders.update or send or receive or cancel; target-dependent auth at propose+confirm — same as HTTP POST …/status), assign_purchase_order (pending confirmation), add_purchase_order_note (pending confirmation; text only)
Help Desk (help-desk.view / help-desk.update / help-desk.assign): get_help_desk_open_tickets, get_help_desk_ticket, update_help_desk_ticket_status (pending confirmation; resolve/close also need help-desk.close at confirm), assign_help_desk_ticket (pending confirmation), add_help_desk_ticket_note (pending confirmation; text only)
Expenses (expenses.view / expenses.update / expenses.assign / expenses.submit / expenses.approve / expenses.reject / expenses.pay / expenses.cancel): get_expense_pending_approval, get_expense, update_expense_status (pending confirmation; visible with expenses.update or submit or approve or reject or pay or cancel; target-dependent auth at propose+confirm — same as HTTP POST …/status; Paid uses pay([]) so Accounting workspaces need paid_from_account_id already on the expense), assign_expense (pending confirmation), add_expense_note (pending confirmation; text only)
Leave Management (leave-management.view / leave-management.approve): get_leave_request, get_pending_leave_requests (status=pending), approve_leave_request (pending confirmation; optional review_notes / deduct_salary; notes required when overriding default salary deduction; mirrors HTTP POST …/approve; non-admins cannot approve their own request), reject_leave_request (pending confirmation; required review_notes; mirrors HTTP POST …/reject). No assign or timeline-note tools.
Tool list/detail rows expose numeric id (SPA paths) and uuid (lookups). Assignee fields use numeric assigned_to (user id) plus assignee_name when present. search_workspace hits also include path for citations; responses may list modules_failed when a provider errors without failing the whole tool. See AI tools guide.