Appearance
labib
10 endpoints.
GET /api/v1/labib/settings
Get Settings
200 — Successful Response
json
{
"properties": {
"enabled": {
"type": "boolean",
"title": "Enabled",
"default": true
},
"kill_switch": {
"type": "boolean",
"title": "Kill Switch",
"default": false
},
"model": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Model"
},
"enable_write_tools": {
"type": "boolean",
"title": "Enable Write Tools",
"default": false
},
"approver_role": {
"type": "string",
"title": "Approver Role",
"default": "admin"
},
"approval_timeout": {
"type": "integer",
"title": "Approval Timeout",
"default": 900
},
"extra_system_prompt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Extra System Prompt"
}
},
"type": "object",
"title": "AiAgentSettingsOut"
}PUT /api/v1/labib/settings
Update Settings
Upsert this tenant's singleton settings row (UniqueConstraint(tenant_id) — created on first write, updated in place after).
Request body
json
{
"properties": {
"enabled": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
],
"title": "Enabled"
},
"kill_switch": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
],
"title": "Kill Switch"
},
"model": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Model"
},
"enable_write_tools": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
],
"title": "Enable Write Tools"
},
"approver_role": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Approver Role"
},
"approval_timeout": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Approval Timeout"
},
"extra_system_prompt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Extra System Prompt"
}
},
"type": "object",
"title": "AiAgentSettingsUpdate"
}200 — Successful Response
json
{
"properties": {
"enabled": {
"type": "boolean",
"title": "Enabled",
"default": true
},
"kill_switch": {
"type": "boolean",
"title": "Kill Switch",
"default": false
},
"model": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Model"
},
"enable_write_tools": {
"type": "boolean",
"title": "Enable Write Tools",
"default": false
},
"approver_role": {
"type": "string",
"title": "Approver Role",
"default": "admin"
},
"approval_timeout": {
"type": "integer",
"title": "Approval Timeout",
"default": 900
},
"extra_system_prompt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Extra System Prompt"
}
},
"type": "object",
"title": "AiAgentSettingsOut"
}422 — Validation Error
json
{
"properties": {
"detail": {
"items": {
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
}
]
},
"type": "array",
"title": "Location"
},
"msg": {
"type": "string",
"title": "Message"
},
"type": {
"type": "string",
"title": "Error Type"
}
},
"type": "object",
"required": [
"loc",
"msg",
"type"
],
"title": "ValidationError"
},
"type": "array",
"title": "Detail"
}
},
"type": "object",
"title": "HTTPValidationError"
}GET /api/v1/labib/skills
List Skills
The skill catalogue active on THIS site — the curated playbooks Labib loads on demand (labib_skills, see app/seeds/labib_skills_seed.py).
The listing shape only (slug/title/category/app_slug/summary), never the procedures: this exists so the Prompt Academy can show a human what Labib has been taught here, and the full text belongs on the labib-skills grid, which is admin-only and editable.
Site-scoped rather than tenant-scoped, matching the table — a skill is platform know-how, not one org's data. App-gated packs are filtered out on a site without that app, exactly as they are for Labib itself, so this page never advertises a skill the chat does not actually have.
Open to any authenticated user rather than admin-only: hiding the explanation of what Labib can do from the people who will be asking it questions helps nobody, and it exposes no business data.
200 — Successful Response
json
{}GET /api/v1/labib/conversations
List Conversations
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
agent_profile_name | query | no |
200 — Successful Response
json
{
"type": "array",
"items": {
"properties": {
"id": {
"type": "string",
"title": "Id"
},
"title": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Title"
},
"status": {
"type": "string",
"title": "Status"
},
"last_message_at": {
"anyOf": [
{
"type": "string",
"format": "date-time"
},
{
"type": "null"
}
],
"title": "Last Message At"
},
"agent_profile_name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Agent Profile Name"
}
},
"type": "object",
"required": [
"id",
"status"
],
"title": "AiAgentConversationSummary"
},
"title": "Response List Conversations Api V1 Labib Conversations Get"
}422 — Validation Error
json
{
"properties": {
"detail": {
"items": {
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
}
]
},
"type": "array",
"title": "Location"
},
"msg": {
"type": "string",
"title": "Message"
},
"type": {
"type": "string",
"title": "Error Type"
}
},
"type": "object",
"required": [
"loc",
"msg",
"type"
],
"title": "ValidationError"
},
"type": "array",
"title": "Detail"
}
},
"type": "object",
"title": "HTTPValidationError"
}POST /api/v1/labib/conversations
Create Conversation
Request body
json
{
"anyOf": [
{
"properties": {
"agent_profile_name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Agent Profile Name"
}
},
"type": "object",
"title": "AiAgentConversationCreate"
},
{
"type": "null"
}
],
"title": "Body"
}201 — Successful Response
json
{
"properties": {
"id": {
"type": "string",
"title": "Id"
},
"title": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Title"
},
"status": {
"type": "string",
"title": "Status"
},
"last_message_at": {
"anyOf": [
{
"type": "string",
"format": "date-time"
},
{
"type": "null"
}
],
"title": "Last Message At"
},
"agent_profile_name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Agent Profile Name"
}
},
"type": "object",
"required": [
"id",
"status"
],
"title": "AiAgentConversationSummary"
}422 — Validation Error
json
{
"properties": {
"detail": {
"items": {
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
}
]
},
"type": "array",
"title": "Location"
},
"msg": {
"type": "string",
"title": "Message"
},
"type": {
"type": "string",
"title": "Error Type"
}
},
"type": "object",
"required": [
"loc",
"msg",
"type"
],
"title": "ValidationError"
},
"type": "array",
"title": "Detail"
}
},
"type": "object",
"title": "HTTPValidationError"
}GET /api/v1/labib/conversations/{conversation_id}
Get Conversation
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
conversation_id | path | yes | string |
200 — Successful Response
json
{
"properties": {
"id": {
"type": "string",
"title": "Id"
},
"title": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Title"
},
"status": {
"type": "string",
"title": "Status"
},
"last_message_at": {
"anyOf": [
{
"type": "string",
"format": "date-time"
},
{
"type": "null"
}
],
"title": "Last Message At"
},
"agent_profile_name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Agent Profile Name"
},
"session_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Session Id"
},
"model": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Model"
},
"messages": {
"items": {
"properties": {
"id": {
"type": "string",
"title": "Id"
},
"idx": {
"type": "integer",
"title": "Idx"
},
"role": {
"type": "string",
"title": "Role"
},
"text": {
"type": "string",
"title": "Text"
},
"turn_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Turn Id"
},
"duration_ms": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Duration Ms"
},
"error": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Error"
},
"created_at": {
"type": "string",
"format": "date-time",
"title": "Created At"
}
},
"type": "object",
"required": [
"id",
"idx",
"role",
"text",
"created_at"
],
"title": "AiAgentMessageOut"
},
"type": "array",
"title": "Messages"
},
"running": {
"type": "boolean",
"title": "Running",
"default": false
},
"action_log": {
"items": {
"properties": {
"id": {
"type": "string",
"title": "Id"
},
"turn_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Turn Id"
},
"tool_name": {
"type": "string",
"title": "Tool Name"
},
"tool_input": {
"anyOf": [
{
"additionalProperties": true,
"type": "object"
},
{
"type": "null"
}
],
"title": "Tool Input"
},
"tool_output": {
"anyOf": [
{
"additionalProperties": true,
"type": "object"
},
{
"type": "null"
}
],
"title": "Tool Output"
},
"outcome": {
"type": "string",
"title": "Outcome"
},
"error": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Error"
},
"duration_ms": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Duration Ms"
},
"before_snapshot": {
"anyOf": [
{
"additionalProperties": true,
"type": "object"
},
{
"type": "null"
}
],
"title": "Before Snapshot"
},
"after_snapshot": {
"anyOf": [
{
"additionalProperties": true,
"type": "object"
},
{
"type": "null"
}
],
"title": "After Snapshot"
},
"approval_request_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Approval Request Id"
},
"created_at": {
"type": "string",
"format": "date-time",
"title": "Created At"
}
},
"type": "object",
"required": [
"id",
"tool_name",
"outcome",
"created_at"
],
"title": "AiAgentActionLogOut"
},
"type": "array",
"title": "Action Log"
},
"pending_approvals": {
"items": {
"properties": {
"id": {
"type": "string",
"title": "Id"
},
"tool_name": {
"type": "string",
"title": "Tool Name"
},
"summary": {
"type": "string",
"title": "Summary",
"default": ""
},
"created_at": {
"type": "string",
"format": "date-time",
"title": "Created At"
},
"expires_at": {
"anyOf": [
{
"type": "string",
"format": "date-time"
},
{
"type": "null"
}
],
"title": "Expires At"
}
},
"type": "object",
"required": [
"id",
"tool_name",
"created_at"
],
"title": "AiAgentPendingApprovalOut"
},
"type": "array",
"title": "Pending Approvals"
}
},
"type": "object",
"required": [
"id",
"status"
],
"title": "AiAgentConversationOut"
}422 — Validation Error
json
{
"properties": {
"detail": {
"items": {
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
}
]
},
"type": "array",
"title": "Location"
},
"msg": {
"type": "string",
"title": "Message"
},
"type": {
"type": "string",
"title": "Error Type"
}
},
"type": "object",
"required": [
"loc",
"msg",
"type"
],
"title": "ValidationError"
},
"type": "array",
"title": "Detail"
}
},
"type": "object",
"title": "HTTPValidationError"
}DELETE /api/v1/labib/conversations/{conversation_id}
Delete Conversation
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
conversation_id | path | yes | string |
200 — Successful Response
json
{}422 — Validation Error
json
{
"properties": {
"detail": {
"items": {
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
}
]
},
"type": "array",
"title": "Location"
},
"msg": {
"type": "string",
"title": "Message"
},
"type": {
"type": "string",
"title": "Error Type"
}
},
"type": "object",
"required": [
"loc",
"msg",
"type"
],
"title": "ValidationError"
},
"type": "array",
"title": "Detail"
}
},
"type": "object",
"title": "HTTPValidationError"
}POST /api/v1/labib/conversations/{conversation_id}/turns
Start Turn
Start one chat turn in the background and return at once; poll GET /conversations/{id} for the reply. Same scope/confinement as :func:send_message, and the disabled / out-of-credits refusals are checked here first so they still come back as an immediate 409 / 402.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
conversation_id | path | yes | string |
Request body
json
{
"properties": {
"text": {
"type": "string",
"title": "Text"
},
"page": {
"anyOf": [
{
"properties": {
"path": {
"type": "string",
"maxLength": 500,
"title": "Path",
"default": ""
},
"title": {
"type": "string",
"maxLength": 200,
"title": "Title",
"default": ""
}
},
"type": "object",
"title": "PageContextIn",
"description": "The page the chat was sent from — see orchestrator ``_turn_context``."
},
{
"type": "null"
}
]
}
},
"type": "object",
"required": [
"text"
],
"title": "SendMessageIn"
}202 — Successful Response
json
{}422 — Validation Error
json
{
"properties": {
"detail": {
"items": {
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
}
]
},
"type": "array",
"title": "Location"
},
"msg": {
"type": "string",
"title": "Message"
},
"type": {
"type": "string",
"title": "Error Type"
}
},
"type": "object",
"required": [
"loc",
"msg",
"type"
],
"title": "ValidationError"
},
"type": "array",
"title": "Detail"
}
},
"type": "object",
"title": "HTTPValidationError"
}POST /api/v1/labib/conversations/{conversation_id}/messages
Send Message
Send one chat turn. Long-running when a write tool is in flight (the approval gate blocks this request up to approval_timeout seconds) — async, so it costs nothing to other requests, and the AI Agent draws from its own LLMClient concurrency pool (pool="ai_agent") so it can't starve Qualifier's calls.
The site scope is enforced on the CONVERSATION, not on this request's body: which agent — and therefore which tool allowlist — a turn runs against is decided by c.agent_profile_id, so confining the row is what actually keeps a narrowed site off the shared agent's tools.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
conversation_id | path | yes | string |
Request body
json
{
"properties": {
"text": {
"type": "string",
"title": "Text"
},
"page": {
"anyOf": [
{
"properties": {
"path": {
"type": "string",
"maxLength": 500,
"title": "Path",
"default": ""
},
"title": {
"type": "string",
"maxLength": 200,
"title": "Title",
"default": ""
}
},
"type": "object",
"title": "PageContextIn",
"description": "The page the chat was sent from — see orchestrator ``_turn_context``."
},
{
"type": "null"
}
]
}
},
"type": "object",
"required": [
"text"
],
"title": "SendMessageIn"
}200 — Successful Response
json
{
"properties": {
"message": {
"properties": {
"id": {
"type": "string",
"title": "Id"
},
"idx": {
"type": "integer",
"title": "Idx"
},
"role": {
"type": "string",
"title": "Role"
},
"text": {
"type": "string",
"title": "Text"
},
"turn_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Turn Id"
},
"duration_ms": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Duration Ms"
},
"error": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Error"
},
"created_at": {
"type": "string",
"format": "date-time",
"title": "Created At"
}
},
"type": "object",
"required": [
"id",
"idx",
"role",
"text",
"created_at"
],
"title": "AiAgentMessageOut"
},
"action_log": {
"items": {
"properties": {
"id": {
"type": "string",
"title": "Id"
},
"turn_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Turn Id"
},
"tool_name": {
"type": "string",
"title": "Tool Name"
},
"tool_input": {
"anyOf": [
{
"additionalProperties": true,
"type": "object"
},
{
"type": "null"
}
],
"title": "Tool Input"
},
"tool_output": {
"anyOf": [
{
"additionalProperties": true,
"type": "object"
},
{
"type": "null"
}
],
"title": "Tool Output"
},
"outcome": {
"type": "string",
"title": "Outcome"
},
"error": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Error"
},
"duration_ms": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Duration Ms"
},
"before_snapshot": {
"anyOf": [
{
"additionalProperties": true,
"type": "object"
},
{
"type": "null"
}
],
"title": "Before Snapshot"
},
"after_snapshot": {
"anyOf": [
{
"additionalProperties": true,
"type": "object"
},
{
"type": "null"
}
],
"title": "After Snapshot"
},
"approval_request_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Approval Request Id"
},
"created_at": {
"type": "string",
"format": "date-time",
"title": "Created At"
}
},
"type": "object",
"required": [
"id",
"tool_name",
"outcome",
"created_at"
],
"title": "AiAgentActionLogOut"
},
"type": "array",
"title": "Action Log"
},
"credits": {
"anyOf": [
{
"properties": {
"metered": {
"type": "boolean",
"title": "Metered",
"default": false
},
"spent": {
"type": "integer",
"title": "Spent",
"default": 0
},
"balance": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Balance"
},
"warning": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Warning"
}
},
"type": "object",
"title": "AiCreditsOut",
"description": "What this turn cost the org's Labib AI allowance, and whether the\ncustomer should be told something about it (2026-09-02, the four-tier\ncredits ladder). ``metered`` is False for an org with no capacity row —\nsee app.apps.erp.services.ai_agent_credits on why those exist."
},
{
"type": "null"
}
]
}
},
"type": "object",
"required": [
"message"
],
"title": "SendMessageOut"
}422 — Validation Error
json
{
"properties": {
"detail": {
"items": {
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
}
]
},
"type": "array",
"title": "Location"
},
"msg": {
"type": "string",
"title": "Message"
},
"type": {
"type": "string",
"title": "Error Type"
}
},
"type": "object",
"required": [
"loc",
"msg",
"type"
],
"title": "ValidationError"
},
"type": "array",
"title": "Detail"
}
},
"type": "object",
"title": "HTTPValidationError"
}GET /api/v1/labib/credits
Get Credits
This org's Labib AI credit balance for the month.
Read-only and cheap — the app polls it to render the usage panel and to grey the composer out before the customer types into a conversation that is going to be refused. An unmetered org returns {"metered": false}; the UI shows no allowance at all for one rather than an empty gauge.
Allowed in BOTH site scopes, unlike Settings and Skills: a Website Builder turn on a portal spends the parent ERP's allowance just like any other, and the routing below was written for exactly that case — so a portal that can spend the balance can also read it.
200 — Successful Response
json
{
"properties": {
"metered": {
"type": "boolean",
"title": "Metered",
"default": false
},
"balance": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Balance"
},
"monthly_allowance": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Monthly Allowance"
},
"sent_this_period": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Sent This Period"
},
"plan_code": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Plan Code"
},
"period_start": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Period Start"
}
},
"type": "object",
"title": "AiCreditsSummaryOut",
"description": "The org's standing Labib AI balance, for the app's usage panel."
}