Browse the docs REST API
Getting Started
Knowledge Base
Activity
Integrations
Plans and Billing
Security and Support
REST API
The REST API lets your own software ask your experts questions over HTTP. Your support portal, an internal tool or a script sends a message, and the expert's answer comes back as JSON. This page is for the person who builds that, and for the admin who creates the key they will use.
It needs the Professional plan. Only a company admin can create or revoke keys.
Create a key
- Go to Integrations under CONNECT and choose REST API.
- Choose New API key.
- Type a Name you will recognise later, such as Support portal.
- Under Experts, decide which experts the key may use. All experts is ticked to begin with, and it also covers any expert you create later. Untick it to tick only the ones you want. Each expert shows its number, such as
#170, which is thechatbot_idyou send in a request. Only experts that are switched on are listed. - Set the Rate limit, in requests per minute. It starts at 60 and can be anything from 1 to 6000.
- Choose Create key.
A window called Copy your API key then shows the key once. Choose Copy, store the key somewhere safe, and choose I've saved it. Aimy keeps only a fingerprint of the key, so nobody can read it back later, including us. If you lose a key, revoke it and create another.
The keys table
The page lists every key in a table, and the Search keys box above it filters by name, key start or expert. The columns are:
| Column | What it shows |
|---|---|
| Name | The name you gave the key. |
| Key | The first few characters, so you can tell keys apart. |
| Expert | All experts, or the experts the key may use. |
| Limit | The rate limit, in requests per minute. |
| Calls (30d) | How many requests the key made in the last 30 days. |
| Last used | The date and time of its latest request, or Never. |
| Status | Active or Revoked. |
Revoke a key
Choose Revoke on the key's row, then Revoke key to confirm. Anything still using that key starts failing straight away, and it cannot be undone. Revoked keys stay in the table so you keep the history.
Base URL and authentication
Every address on this page starts with:
https://aimy.com.my/expert
Send the key as a Bearer token on every request:
Authorization: Bearer aimy_sk_your_key_here
If your tool cannot set that header, send the key in an X-API-Key header instead:
X-API-Key: aimy_sk_your_key_here
A key that is missing, wrong or revoked gets the same 401 answer in every case, so the response never tells anyone which one it was.
Ask a question
POST /api/v1/chat
curl -X POST https://aimy.com.my/expert/api/v1/chat \
-H "Authorization: Bearer YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"message": "What is the refund policy?",
"chatbot_id": 170
}'
The request body is JSON with these fields.
| Field | Required | What it does |
|---|---|---|
message | Yes | The question. Up to 8000 characters. |
chatbot_id | Usually | Which expert should answer. You can leave it out only when the key is limited to exactly one expert. Get the ids from the experts list below. |
conversation_id | No | Leave it out to start a new conversation. Send the same value again to carry on with it. |
user_name | No | The name of the person asking, up to 100 characters. History and the handoff email show it. |
user_email | No | Their email address, up to 100 characters. |
user_contact | No | A phone number or other contact detail, up to 100 characters. |
request_handoff | No | Set to true to ask for a person to take over the conversation. See Handing a chat to a person. |
A successful answer looks like this.
{
"success": true,
"answer": "Refunds are available within 30 days ...",
"conversation_id": "8f2c1a7b9d0e4f6a1b2c3d4e",
"chatbot": { "id": 170, "name": "Support Expert" },
"citations": [ { "pdf_id": 31, "name": "policy.pdf", "page": 4, "score": 0.81, "snippet": "...", "kind": "retrieval", "url": null } ],
"sources": [ { "document": "policy.pdf", "page": 4, "preview": "...", "score": 0.81 } ]
}
citations and sources follow the Show sources switch on the expert. With the switch off, both come back as empty lists. They are always present, so your code does not need to check whether they exist.
A citation object has these fields.
| Field | What it holds |
|---|---|
pdf_id | The id of the document. It is 0 for a web result. |
name | The document name, or the page title for a web result. |
page | The page number, or null when the source has no pages, which is always so for a web result. |
score | How closely the passage matched the question, or null. |
snippet | The passage the answer drew on. |
kind | retrieval for a passage from your documents, web for a web search result, or analysis when the expert worked something out from a spreadsheet. |
url | The address of the page for a web result, otherwise null. |
An answer has at most 6 citations. An answer that is a refusal, such as "I don't have that information", usually comes back with none.
The expert answers with the same documents and settings it uses everywhere else.
Keep a conversation going
The first answer returns a conversation_id. Send it back with the next message and the expert remembers what was said.
# First call: no conversation_id
{"message": "Do you ship to Sabah?", "chatbot_id": 170}
-> {"answer": "Yes ...", "conversation_id": "8f2c1a7b9d0e4f6a1b2c3d4e"}
# Follow up: same conversation_id
{"message": "How long does it take?",
"chatbot_id": 170,
"conversation_id": "8f2c1a7b9d0e4f6a1b2c3d4e"}
You can also make up your own id, such as a ticket number. It must be 1 to 64 characters, using letters, digits, _ or -. A new id starts a fresh conversation that knows nothing about the others.
The conversations appear in History, titled with the key's name unless you send user_name. See History.
List your experts
GET /api/v1/experts
curl https://aimy.com.my/expert/api/v1/experts \
-H "Authorization: Bearer YOUR_KEY"
{
"success": true,
"experts": [
{ "id": 170, "name": "Support Expert", "description": "...", "active": true, "documents": 12 }
],
"pinned": false
}
The list holds only the experts this key may use. It includes experts that are switched off, shown with "active": false. Asking a question of one of those returns 409 with the code chatbot_inactive. pinned is true when the key is limited to exactly one expert, which means you can leave chatbot_id out when you ask questions.
documents is a count of every processed document that belongs to the expert's owner. It is not limited to the documents attached to that expert, so two experts with the same owner show the same number.
The older address /api/v1/chatbots still works and returns the same list, under the name chatbots instead of experts.
When a person takes over
If someone on your team has taken over a conversation, the expert stops answering it. /api/v1/chat then returns "live_agent": true with an empty answer. That is not an error, so do not retry the message. Ask for the person's replies instead:
GET /api/v1/messages?conversation_id=8f2c1a7b9d0e4f6a1b2c3d4e&chatbot_id=170&after_id=0
curl "https://aimy.com.my/expert/api/v1/messages?conversation_id=8f2c1a7b9d0e4f6a1b2c3d4e&chatbot_id=170" \
-H "Authorization: Bearer YOUR_KEY"
{
"success": true,
"live_agent": true,
"status": "assigned",
"messages": [
{ "id": 41, "sender": "agent", "body": "Hi, I can help with that.", "created_at": "2026-10-12 10:15:02" }
],
"last_id": 41
}
conversation_id is required. chatbot_id follows the same rule as for chat. Keep the last_id you get back and send it as after_id on the next call, so you only receive new messages. sender is agent for a person and system for a notice. Your own messages are not echoed back. status is open while the conversation waits for someone and assigned once a person has it.
When the person finishes, live_agent becomes false with no messages, and the expert answers again from the next message.
Errors
Every failure has the same shape. Branch on code. The message is for people and may be reworded.
{
"success": false,
"error": { "code": "rate_limited", "message": "Too many requests. This key allows 60 per minute.", "retry_after": 60 }
}
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | A field is missing or malformed. The message says which. |
| 400 | chat_failed | The expert could not answer this message. |
| 401 | unauthorized | The key is missing, wrong or revoked. |
| 402 | quota_exceeded | The account has used up its allowance for the month, or has no active plan. |
| 403 | forbidden_plan | The account's plan does not include the REST API. |
| 403 | chatbot_not_allowed | This key may not use that expert. |
| 404 | chatbot_not_found | No such expert in your company. |
| 405 | method_not_allowed | Wrong HTTP method. Chat is POST. Experts and messages are GET. |
| 409 | chatbot_inactive | The expert is switched off. It still appears in the experts list, marked inactive. |
| 429 | rate_limited | Too many requests. Wait for Retry-After. |
| 500 | internal_error | The request could not be completed. |
| 502 | upstream_error | The expert service did not return a valid response. Try again. |
| 503 | busy | The service is under load. Try again shortly. |
Rate limits
Each key has its own limit, counted over a window of 60 seconds. Requests that fail still count, as long as the key was accepted. Once the key is accepted and the plan check passes, every response carries two headers:
X-RateLimit-Limitis the key's limit.X-RateLimit-Remainingis how many requests are left in the current window.
The headers are missing from 405, 401 and 403 forbidden_plan answers, because those are refused before the limit is checked.
A 429 also carries a Retry-After header and a retry_after field in the error. Both are always 60 seconds. The rate limit is separate from your plan's monthly allowance, so you can be inside the first and still get quota_exceeded.
Usage and history
Answers count toward your plan's allowance like any other channel, and Usage lists API as its own channel. The keys table shows each key's last use and its calls over the last 30 days. Aimy keeps the log of individual requests for 30 days.