API docs
API reference for the Paymenter AIAssistant extension.
AI Assistant API
API reference for the Paymenter AIAssistant extension.
The API is intended for server-side integrations such as Discord bots, helpdesk bridges, custom dashboards, and automation services. It uses Paymenter's admin API key middleware and never exposes provider API keys.
Base URL
/api/v1/ai-assistantExample full URL:
https://billing.example.com/api/v1/ai-assistant/statusAuthentication
Send a Paymenter admin API key as a bearer token:
Authorization: Bearer <admin-api-key>
Accept: application/json
Content-Type: application/jsonPermissions
Grant the narrowest permission your integration needs.
| Permission | Allows |
|---|---|
admin.ai_assistant.view | Read status, usage, chats, messages, and logs |
admin.ai_assistant.create | Create chats and send prompts |
admin.ai_assistant.update | Update, close, or reopen chats |
admin.ai_assistant.delete | Archive chats |
Keys with * or admin.ai_assistant.* are also accepted.
Response Format
Successful non-paginated responses use:
{
"data": {}
}Paginated responses also include meta:
{
"data": [],
"meta": {
"current_page": 1,
"last_page": 1,
"per_page": 25,
"total": 0
}
}Errors use Paymenter/Laravel JSON responses where possible:
{
"message": "The API key is missing the admin.ai_assistant.view permission."
}Modes
The extension exposes two prompt modes.
| Mode | Endpoint | Behavior |
|---|---|---|
| Personalized | POST /messages or POST /chats/{chat_public_id}/messages | Uses a Paymenter user, chat history, and user-scoped tools. |
| General | POST /general/messages | API-only public help. No user_id, no saved chat, no private account tools. Public portal/catalog tools are allowed. |
Use personalized mode for account-specific support. Use general mode for public FAQ/product/catalog bots.
Endpoints
| Method | Path | Permission |
|---|---|---|
GET | /status | admin.ai_assistant.view |
GET | /logs | admin.ai_assistant.view |
GET | /users/{user_id}/usage | admin.ai_assistant.view |
GET | /users/{user_id}/chats | admin.ai_assistant.view |
POST | /users/{user_id}/chats | admin.ai_assistant.create |
GET | /users/{user_id}/chats/active | admin.ai_assistant.view |
POST | /messages | admin.ai_assistant.create |
POST | /general/messages | admin.ai_assistant.create |
GET | /chats/{chat_public_id} | admin.ai_assistant.view |
PATCH | /chats/{chat_public_id} | admin.ai_assistant.update |
POST | /chats/{chat_public_id}/close | admin.ai_assistant.update |
POST | /chats/{chat_public_id}/reopen | admin.ai_assistant.update |
POST | /chats/{chat_public_id}/archive | admin.ai_assistant.delete |
GET | /chats/{chat_public_id}/messages | admin.ai_assistant.view |
POST | /chats/{chat_public_id}/messages | admin.ai_assistant.create |
Status
Get Status
GET /api/v1/ai-assistant/statusReturns persistence table availability, enabled provider counts, request limits, and enabled feature flags.
Example response:
{
"data": {
"api_version": "1",
"persistence": {
"chats": true,
"messages": true,
"logs": true,
"providers": true
},
"providers": {
"total": 2,
"enabled": 1
},
"limits": {
"max_prompt_chars": 3000,
"max_history_messages": 20,
"max_requests_per_minute": 12,
"monthly_request_limit": 2000
},
"features": {
"ticket_creation_tool": true,
"logging": true
}
}
}Usage
Get User Usage
GET /api/v1/ai-assistant/users/{user_id}/usageReturns monthly request usage for a Paymenter user.
Example response:
{
"data": {
"user_id": 42,
"monthly_limit": 2000,
"monthly_used": 14,
"monthly_remaining": 1986,
"resets_at": "2026-07-01T00:00:00+00:00"
}
}Chats
List User Chats
GET /api/v1/ai-assistant/users/{user_id}/chats?status=active&search=billing&per_page=25&page=1Query parameters:
| Parameter | Type | Notes |
|---|---|---|
status | string | Optional. active, closed, or archived. |
search | string | Optional. Searches chat title and public id. |
per_page | integer | Optional. 1-100, default 25. |
page | integer | Optional pagination page. |
Get Or Create Active Chat
GET /api/v1/ai-assistant/users/{user_id}/chats/active?create=trueQuery parameters:
| Parameter | Type | Notes |
|---|---|---|
create | boolean | Defaults to true. If false, returns 404 when no active chat exists. |
Create Chat
POST /api/v1/ai-assistant/users/{user_id}/chatsRequest body:
{
"title": "Billing follow-up",
"status": "active",
"metadata": {
"external_thread_id": "thread_123"
}
}Fields:
| Field | Type | Notes |
|---|---|---|
title | string | Optional, max 180 characters. |
status | string | Optional. active, closed, or archived. Defaults to active. |
metadata | object | Optional integration metadata. |
Get Chat
GET /api/v1/ai-assistant/chats/{chat_public_id}Update Chat
PATCH /api/v1/ai-assistant/chats/{chat_public_id}Request body:
{
"title": "Resolved billing question",
"status": "closed",
"metadata": {
"external_thread_id": "thread_123"
}
}Close, Reopen, Or Archive Chat
POST /api/v1/ai-assistant/chats/{chat_public_id}/close
POST /api/v1/ai-assistant/chats/{chat_public_id}/reopen
POST /api/v1/ai-assistant/chats/{chat_public_id}/archiveMessages
List Chat Messages
GET /api/v1/ai-assistant/chats/{chat_public_id}/messages?per_page=25&page=1Query parameters:
| Parameter | Type | Notes |
|---|---|---|
after_id | integer | Optional. Return messages newer than this id. |
before_id | integer | Optional. Return messages older than this id. |
per_page | integer | Optional. 1-100, default 25. |
page | integer | Optional pagination page. |
Messages are returned oldest-first inside each page.
Send Prompt To Existing Chat
POST /api/v1/ai-assistant/chats/{chat_public_id}/messagesThe chat owner becomes the scoped Paymenter user. Personalized tools are scoped to that user.
Request body:
{
"prompt": "Can you check whether I have unpaid invoices?",
"source": "external_client",
"external_id": "msg_123",
"extra_prompt": "Optional extra system instructions for this request.",
"enable_tools": true,
"include_ticket_creation_tool": true,
"force_first_tool_call": true,
"max_tool_rounds": 4
}Fields:
| Field | Type | Notes |
|---|---|---|
prompt | string | Required. Max length follows the extension setting. |
source | string | Optional, max 80 characters. Defaults to api. |
external_id | string | Optional, max 120 characters. |
extra_prompt | string | Optional, max 4000 characters. Appended to system instructions for this request. |
enable_tools | boolean | Optional. Defaults to true. |
include_ticket_creation_tool | boolean | Optional. Defaults to the extension ticket tool setting. |
force_first_tool_call | boolean | Optional. Defaults to automatic detection. |
max_tool_rounds | integer | Optional. 0-24. |
Send Prompt And Resolve Chat
POST /api/v1/ai-assistant/messagesUse this endpoint when the integration does not already know the chat public id.
Request body:
{
"user_id": 42,
"prompt": "Open a support ticket about my suspended service.",
"chat_public_id": null,
"create_chat": true,
"new_chat": false,
"title": "Service help",
"metadata": {
"external_thread_id": "thread_123"
},
"source": "external_client",
"external_id": "msg_123",
"extra_prompt": "Optional extra system instructions for this request.",
"enable_tools": true,
"include_ticket_creation_tool": true,
"force_first_tool_call": false,
"max_tool_rounds": 4
}Additional fields:
| Field | Type | Notes |
|---|---|---|
user_id | integer | Required. Paymenter user id. |
chat_public_id | string | Optional. Existing chat to use. |
create_chat | boolean | Optional. Defaults to true. |
new_chat | boolean | Optional. If true, always creates a fresh chat. |
title | string | Optional title for newly created chats. |
metadata | object | Optional metadata for newly created chats. |
Chat resolution order:
- If
new_chat=true, create a fresh chat. - If
chat_public_idis provided, use that chat after confirming it belongs touser_id. - Otherwise use the active chat for
user_id. - If no active chat exists and
create_chat=true, create a chat.
Example success response:
{
"data": {
"status": "success",
"chat": {
"id": 12,
"public_id": "f4dfcb61-d6e7-4a7f-8efe-4be9d11b3178",
"user_id": 42,
"title": "Service help",
"status": "active",
"messages_count": 2,
"last_activity_at": "2026-06-13T12:00:00+00:00",
"closed_at": null,
"metadata": {}
},
"user_message": {
"id": 1001,
"role": "user",
"content": "Can you check whether I have unpaid invoices?"
},
"assistant_message": {
"id": 1002,
"role": "assistant",
"content": "You have one unpaid invoice..."
},
"provider": {
"id": 1,
"name": "Primary Provider",
"model": "gpt-4.1-mini"
},
"usage": {
"prompt_tokens": 1200,
"completion_tokens": 180,
"total_tokens": 1380
},
"tool_calls": 1
}
}If every provider fails, the endpoint returns HTTP 502 and still stores a user-visible assistant error message in the chat.
General Prompt API
Send General Prompt
POST /api/v1/ai-assistant/general/messagesUse this API-only endpoint for general, non-account-specific answers. It does not accept user_id, does not create or read chats, does not load history, and does not persist user/assistant messages.
General mode can use public tools only:
| Tool | Purpose |
|---|---|
get_portal_info | Public portal basics, company name, currencies, ticket departments, common links. |
get_portal_page_status | Public page availability such as cart, tickets, account, KYC detection signals. |
get_portal_links | Public portal, category, product, and checkout links. |
list_catalog_categories | Public product categories and visible product counts. |
list_catalog_products | Visible products, plans, pricing, product links, and checkout links. |
Private account tools are not exposed in general mode. This includes invoices, tickets, ticket creation, account info, and purchased services.
Request body:
{
"prompt": "Which hosting plans are available?",
"source": "discord_bot",
"external_id": "discord_message_123",
"extra_prompt": "Use a short support-friendly tone."
}Fields:
| Field | Type | Notes |
|---|---|---|
prompt | string | Required. Max length follows the extension setting. |
source | string | Optional, max 80 characters. Defaults to api_general. |
external_id | string | Optional, max 120 characters. |
extra_prompt | string | Optional, max 4000 characters. Treated as style guidance only; it cannot enable private account tools. |
Example success response:
{
"data": {
"status": "success",
"mode": "general",
"assistant_message": {
"role": "assistant",
"content": "The available plans are Starter, Pro, and Business..."
},
"provider": {
"id": 1,
"name": "Primary Provider",
"model": "gpt-4.1-mini"
},
"usage": {
"prompt_tokens": 450,
"completion_tokens": 90,
"total_tokens": 540
},
"tool_calls": 1,
"latency_ms": 1800
}
}Example failed provider response:
{
"data": {
"status": "failed",
"mode": "general",
"assistant_message": {
"role": "assistant",
"content": "The AI provider request failed. Please try again later."
},
"error": {
"message": "The AI provider request failed. Please try again later.",
"internal_message": null
},
"latency_ms": 1200
}
}Logs
List Logs
GET /api/v1/ai-assistant/logs?user_id=42&status=success&chat_public_id=<id>&per_page=25Query parameters:
| Parameter | Type | Notes |
|---|---|---|
user_id | integer | Optional. Filter by Paymenter user. |
provider_id | integer | Optional. Filter by provider id. |
status | string | Optional. Usually success or failed. |
chat_public_id | string | Optional. Filter by chat public id. |
per_page | integer | Optional. 1-100, default 25. |
page | integer | Optional pagination page. |
General prompt calls are not written to ext_ai_assistant_logs because the existing log table is user-scoped. Personalized prompt calls are logged when extension logging is enabled.
Rate Limits
Personalized prompt endpoints use:
- Per-user per-minute limit from
max_requests_per_minute. - Monthly per-user request limit from
monthly_request_limit.
General prompt endpoint uses:
- Per-admin-key and IP per-minute limit from
max_requests_per_minute. - No monthly user limit because no Paymenter user is attached.
Provider-level limits still apply to both modes.
Security Notes
- Store admin API keys server-side only.
- Use
admin.ai_assistant.createfor prompt-only integrations. - Use
admin.ai_assistant.viewonly when the integration needs status, logs, chats, or message reads. - In personalized mode, every response is generated as the resolved Paymenter user. Private tools remain user-scoped.
- Do not pass a chat public id that belongs to a different
user_id; the API rejects that pairing. - In general mode, do not send account identifiers or private user data. The endpoint is designed for public product, plan, portal, and FAQ style answers.
- Provider credentials are never returned by the API.
- Use
source,external_id, andmetadatato map external messages or threads. Do not store secrets in these fields.
cURL Examples
General Product/Plan Question
curl -X POST "https://billing.example.com/api/v1/ai-assistant/general/messages" \
-H "Authorization: Bearer $PAYMENTER_API_KEY" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"prompt": "Which plans are available?",
"source": "discord_bot",
"external_id": "msg_123"
}'Personalized User Prompt
curl -X POST "https://billing.example.com/api/v1/ai-assistant/messages" \
-H "Authorization: Bearer $PAYMENTER_API_KEY" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"user_id": 42,
"prompt": "Do I have any unpaid invoices?",
"create_chat": true,
"source": "external_client"
}'Read Chat Messages
curl "https://billing.example.com/api/v1/ai-assistant/chats/f4dfcb61-d6e7-4a7f-8efe-4be9d11b3178/messages" \
-H "Authorization: Bearer $PAYMENTER_API_KEY" \
-H "Accept: application/json"