Connect Your API
Open in CMSThis tutorial walks you through registering an external API so your agent can call it automatically during conversations. You give Atlas, Kirana Group's operations assistant, a way to check live project data: when someone asks "What is the status of KG-2041?", Atlas calls Kirana's Projects API and answers with the status, approval stage, progress and budget. By the end, you have a working API action, know how to test it without leaving qlar, and know which advanced options exist when you need them.
Before you start
- Your agent already answers correctly from its own knowledge. If not, start with Teach Your Agent.
- You know how to test and publish changes. See Preview and Publish Changes.
- You have the URL of the API you want to call, plus its credentials if it needs any. For this tutorial you only need internet access: the example API is a public, read-only sample.
- Looking for a ready-made integration instead? Check Using Plugins first.
How qlar Calls Your API
qlar does not require you to write any code to connect an API. Instead, you describe the API in plain language and qlar passes that description to the AI model as a function tool. Here is what happens on every conversation turn:
- The AI reads the user's message and decides, based on your description, whether it needs to call the API to give an accurate answer.
- qlar makes the HTTP request to your endpoint, filling in the parameters it extracted from the conversation.
- Your server responds with data in any format (JSON, plain text, and so on).
- The AI reads the response and turns it into a natural-language reply.
This cycle happens automatically. You register the API once; the AI decides when and how to use it.
Scenario Used in This Tutorial
Kirana keeps the status of every project in its Projects API. This tutorial uses a public, read-only sample of that API, so you can follow along and test the result right away. It knows three projects:
| Project code | Project |
|---|---|
KG-2041 | Surabaya warehouse expansion |
KG-2052 | Jakarta head office fit-out |
KG-2063 | Bekasi rooftop solar |
Any other code returns HTTP 404 Not Found.
| Field | Value |
|---|---|
| Action name | get_project_status |
| Purpose | Return the status, progress, budget and current approval stage of one project |
| Endpoint | GET https://stpusakaappassetprod.blob.core.windows.net/docs-media/samples/kirana-api/projects/{projectCode}.json |
| Parameter | projectCode, the project code, for example KG-2041 |
| Authentication | None |
Once you are comfortable with the steps, swap in your own endpoint.
Step 1: Open Custom API
Move your pointer over the icon sidebar on the left edge of the CMS to expand it (on a narrow screen, click the menu button (β°) at the top left instead). Expand Tools, then click Custom API.
The Custom API page loads. At the top are four buttons: Import OpenAPI Schema, Add New API, Add Category and Set Authentication. Below them is the list of API actions already registered for this agent.
Step 2: Add a New API
Click Add New API. The New API panel slides in from the right with three sections: Basic Information, Authentication and Properties. The screenshot shows the panel as it looks when you have finished this tutorial; the next steps fill it in section by section.

Step 3: Fill in the Action Name and Description
The Basic Information section is the most important part. The AI uses the name and description to decide when to call this API, so clarity here directly affects how well the integration works.
Action Name
Type get_project_status in the Action Name field.
Tip: Use
snake_caseand keep names short but specific. The AI model reads this name, soget_project_statusis clearer thanprojectsorlookup.
Description
In the Description field, explain what the API returns and when it should be called. Write it as if you are briefing the AI:
Returns the status, progress, budget and current approval stage of one Kirana Group project. Call it when the user asks about a project by its code, for example "What is the status of KG-2041?".

Step 4: Set the HTTP Method and Endpoint
-
Set HTTP Method to
GET. -
In Target URI, enter the full URL of the endpoint:
https://stpusakaappassetprod.blob.core.windows.net/docs-media/samples/kirana-api/projects/{projectCode}.json
Note:
{projectCode}is a path parameter. qlar replaces it with the value the AI extracts from the conversation, so "What is the status of KG-2041?" calls.../projects/KG-2041.json. You define the parameter as a property in Step 8.
Step 5: Configure a Loading Text
While qlar waits for your API to respond, it shows a short message to the user. A new API starts with Loading Text Type set to Auto.
-
Set Loading Text Type to Fixed.
-
In the Loading Text field that appears, enter:
Checking the project status...
Tip: Keep loading text short and action-oriented. It disappears as soon as the answer arrives. See Loading Text Types for the other options.
Step 6: Add Rules and a Response Prompt
Both fields are optional, but they make the answer far more reliable.
Rules tell the AI how to interpret the response: what to use, and what to do when something is missing. Enter:
Use only the fields in the response. If the project is not found (HTTP 404), say that the code is unknown and ask the user to check it (codes look like KG-2041).
Response Prompt tells the AI how to present the result. Enter:
Start with the status and the current approval stage, then progress, budget spent versus total, and the latest update. Keep it under six lines.
Leave Additional Properties and Send Metadata off. Both are explained under Advanced Features.
Step 7: Skip Authentication (Public Endpoint)
The sample endpoint is public. In the Authentication section, leave Auth Type on None and move on.
If your own API requires credentials, see Authentication Options at the bottom of this page.
Step 8: Add a Property
Properties tell the AI which values to extract from the conversation and pass to your API. In the Properties section, click Add New Property. A new property row appears. Fill it in:
| Field | Value |
|---|---|
| Property Name | projectCode |
| Property Type | String |
| Location | Path |
| Required Parameter | On |
| Property Enum | Leave empty |
| Property Description | Kirana project code in the form KG-0000, for example KG-2041. Use upper case. |

Tip: The Property Description is read by the AI. A precise description with an example of a valid value reduces the chance of the AI sending something unexpected, such as
kg 2041instead ofKG-2041.
About Location: Path means the value replaces
{projectCode}in the URL. Use Querystring for parameters appended as?key=value, Header for values sent as an HTTP header, and Body for the payload of aPOSTrequest. Property Enum limits the property to a fixed list of values: type a value and press Enter to add it.
Step 9: Add the API to the List
Click Add New API at the bottom of the panel. The panel closes and get_project_status appears in the list, with its description, a None chip for its authentication, an on/off switch and a delete button.

Note: The API is only in the form so far. It is not saved yet.
Step 10: Save All Changes
Click Save all changes at the bottom of the Custom API page. The CMS asks Are you sure to save your changes? Click Yes. A message confirms Custom API successfully updated!
The change is saved as a draft. You can try get_project_status in the preview right away, and real users get it after you click Publish in the top bar.
Step 11: Test the API
Before you try it in a conversation, check that qlar can reach the endpoint and that the response looks right.
- In the list, click the
get_project_statusrow to open its panel again. - At the bottom left of the panel, click Test API. The panel changes to Test API: get_project_status and shows the API Details (method, authentication, endpoint, description).
- Under Parameters, type
KG-2041in Project Code. - Click Call API.
- Under API Response, a
200 OKbadge appears, with Response Headers and Response Body below it.
The response body for KG-2041 looks like this:
{
"projectCode": "KG-2041",
"name": "Surabaya warehouse expansion",
"site": "Surabaya",
"projectManager": "Dewi Lestari",
"status": "Delayed",
"percentComplete": 62,
"budgetUsd": 2500000,
"spentUsd": 1480000,
"approval": {
"stage": "Regional Director",
"waitingSince": "2026-09-29",
"nextApprover": "CFO"
},
"latestUpdate": "Steel delivery slipped two weeks; new completion date 15 December 2026.",
"plannedCompletion": "2026-11-30",
"forecastCompletion": "2026-12-15"
}

Click Back to Configuration to return to the form.
Tip: Also try
KG-9999. You get404, which is exactly the case your Rules handle: Atlas should say the code is unknown and ask the user to check it.
Check the result
In the preview panel on the right, click the reload button to start a fresh conversation and ask:
What is the status of project KG-2041?
The loading text Checking the project status... appears briefly. Then Atlas answers in the layout of your Response Prompt:
- Status: Delayed, approval is at the Regional Director stage; next approver: CFO.
- Progress: 62% complete.
- Budget: US$1.48M spent of US$2.5M.
- Latest update: steel delivery slipped two weeks; forecast completion is 15 December 2026.

Then ask about a code that does not exist, for example "And KG-9999?". Atlas should say the code is unknown and ask you to check it, instead of inventing a project.
If the answer is not right:
- Atlas answers without calling the API. Make the Description more specific about when to call it, and check that the API's switch in the list is on.
- Atlas says the project cannot be found for a valid code. Open Test API and call it with the same code. A
404usually means the code reached the API in the wrong form; tighten the Property Description.
When the answers look right, click Publish so real users get the new API. See Preview and Publish Changes.
Advanced Features
The steps above cover the most common setup. The sections below explain features you may need as your integration grows.
Import APIs from an OpenAPI Schema
Instead of adding actions one by one, you can register several at once from an existing OpenAPI (Swagger) definition. Click Import OpenAPI Schema and follow the four steps of the wizard. See Import an OpenAPI Schema for the full walkthrough with Kirana's sample schema.
Set Authentication for Multiple APIs at Once
When several APIs share the same credentials, for example all actions imported from one schema, click Set Authentication to configure the authentication once and apply it to every API you tick. See Set Authentication.
Authentication Options
Select the Auth Type in the Authentication section and the required fields appear.
| Auth Type | When to use |
|---|---|
| None | Public endpoints, no credentials needed |
| Bearer | APIs secured with a static Bearer token |
| Basic | APIs that use a username and password |
| Custom | APIs with a custom header name (for example X-API-Key) |
| Oauth | APIs secured with OAuth 2.0 (Client Credentials or Google Service Account) |
For Bearer, enter the token in the Key field.
For Basic, enter a Username and Password.
For Custom, enter the Header Name (for example X-API-Key) and the Key value.
For Oauth, select the provider type, enter the Token URL, and supply the credentials for your grant type.
Response Prompt
The Response Prompt field describes how the AI should present the API result to the user: tone, format, and level of detail. This is separate from Rules, which governs how the AI interprets the raw data.
Example for Atlas:
Start with the status and the current approval stage, then progress, budget spent versus total, and the latest update. Keep it under six lines.
| Field | Controls |
|---|---|
| Rules | How the AI processes and constrains the API response (logic, fallbacks) |
| Response Prompt | How the AI formats and delivers the result to the user (tone, structure) |
Send Metadata
When Send Metadata is on, qlar attaches conversation and identity context to every request sent to your API. Use this when your API needs to personalize the response based on the user's identity, channel, or permissions.
Metadata is always sent as HTTP headers, and is also included in the query string (for GET/DELETE) or the JSON request body (for POST/PUT/PATCH), depending on the action's HTTP Method.
Always sent as headers
| Header | Description |
|---|---|
X-AgentId | ID of the agent handling the conversation |
X-UserId | Unique identifier of the user chatting |
X-UserEmail | Email of the user chatting (empty string if not available) |
X-InquiryMessageId | ID of the message that triggered the API call |
X-MediaType | Channel the user is on (web, WhatsApp, etc.) |
X-TargetAgentId | ID of the target agent, if the conversation was handed off |
X-MessengerAccount-Product | Messenger product being used, only sent if available |
X-MessengerAccount-AccountCode | Specific messenger account code, only sent if available |
X-User-Permissions | JSON array of the user's permissions, only sent if non-empty |
Also sent in the query string, for GET/DELETE
| Query parameter | Same value as |
|---|---|
agentId | X-AgentId |
userId | X-UserId |
userEmail | X-UserEmail |
inquiryMessageId | X-InquiryMessageId |
mediaType | X-MediaType |
targetAgentId | X-TargetAgentId |
Note: the messenger account and user permissions fields are only available via the headers above for
GET/DELETErequests. They are not appended to the query string.
Also sent in the JSON body, for POST/PUT/PATCH
A metadata object is added alongside your declared body parameters:
{
"metadata": {
"AgentId": "...",
"UserId": "...",
"UserEmail": "...",
"InquiryMessageId": "...",
"MediaType": "...",
"TargetAgentId": "...",
"MessengerAccount": { "Product": "...", "AccountCode": "..." },
"UserPermissions": ["..."]
}
}
Note: unlike the headers and query string above, the fields inside the
metadatabody object use PascalCase (for exampleAgentId, notagentId).MessengerAccountisnullwhen the user isn't on a messenger channel.
Example Server Implementation
The examples below show a server you build to receive calls from qlar. The sample Projects API used earlier is a set of static files and needed no server code at all. The scenario here is a simple order_status action: given an orderId, it looks up and returns the order's shipping status. Authentication uses Auth Type: Custom, with a header named X-Api-Key.
Use the controls to see how the request and server code change with HTTP Method, Send Metadata, and Media Type:
Sample request qlar sends
POST /api/order-status HTTP/1.1
Content-Type: application/json
X-Api-Key: sk_live_abc123
X-AgentId: 3fa85f64-5717-4562-b3fc-2c963f66afa6
X-UserId: user_8231
X-UserEmail:
X-InquiryMessageId: msg_9f21
X-MediaType: web
X-TargetAgentId:
X-User-Permissions: []
{
"orderId": "ORD-1001",
"metadata": {
"AgentId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"UserId": "user_8231",
"UserEmail": "",
"InquiryMessageId": "msg_9f21",
"MediaType": "web",
"TargetAgentId": "",
"MessengerAccount": null,
"UserPermissions": []
}
}Server implementation
const express = require("express");
const app = express();
app.use(express.json());
const API_KEY_HEADER = "x-api-key";
const API_KEY = process.env.CUSTOM_API_KEY; // must match the "Key" set in CMS Auth Type: custom
const ORDERS = {
"ORD-1001": { status: "shipped", carrier: "JNE", eta: "2026-08-02" },
};
app.post("/api/order-status", (req, res) => {
// 1. Verify the custom auth header configured in CMS (Auth Type: custom)
if (req.get(API_KEY_HEADER) !== API_KEY) {
return res.status(401).json({ error: "Invalid or missing API key" });
}
// 2. Read the declared parameter
const { orderId } = req.body;
if (!orderId) return res.status(400).json({ error: "orderId is required" });
// Metadata is always sent as headers (also duplicated in the query string for
// GET/DELETE, or under "metadata" in the JSON body for POST/PUT/PATCH)
const agentId = req.get("X-AgentId");
const userId = req.get("X-UserId");
const inquiryMessageId = req.get("X-InquiryMessageId");
const mediaType = req.get("X-MediaType"); // "web" | "whatsapp" | ...
const userPermissions = JSON.parse(req.get("X-User-Permissions") || "[]");
console.log("Call from agent " + agentId + ", user " + userId + ", channel " + mediaType);
// 3. Look up the order
const order = ORDERS[orderId];
if (!order) return res.status(404).json({ error: "Order " + orderId + " not found" });
// Personalise the response by channel when metadata is available
if (mediaType === "whatsapp") {
// Keep WhatsApp replies short; no rich formatting
return res
.status(200)
.send("Order " + orderId + " is " + order.status + " via " + order.carrier + ", arriving " + order.eta + ".");
}
return res.status(200).json({ orderId, ...order });
});
app.listen(3000, () => console.log("Listening on :3000"));
Note:
Media Typeonly changes the sample values shown above. The server code itself readsX-MediaTypeat runtime and already handles bothweband
Additional Properties
By default, the AI is restricted to the parameters you define in the Properties section. Turning on Additional Properties allows the AI to pass extra key-value pairs beyond what is explicitly listed. This is useful for flexible or dynamic API contracts.
Categories and the On/Off Switch
Click Add Category to group related APIs in the list, for example Projects and Procurement. APIs without a category are listed under Uncategorized. Drag a row by its handle to reorder it.
Each row has a switch. Turn it off to stop the agent from using that API without deleting its configuration. Like every change on this page, it takes effect after Save all changes (and Publish for real users).
Loading Text Types
| Type | Behavior |
|---|---|
| Auto | The default. The AI writes a short loading message that fits the current request. |
| None | No loading message is shown to the user. |
| Fixed | The exact text you enter in Loading Text is shown every time. |
| Dynamic | The AI writes the loading message, guided by the instruction you enter in Loading Text Prompt. |
Fixed gives you full control over the wording. Dynamic is useful when one API action is used in different contexts and a single fixed text would feel out of place.
Next steps
- Next in the journey: Import an OpenAPI Schema, to register many API actions at once from an existing spec.
- Set Authentication: apply the same credentials to many APIs in one step.
- Connect an MCP Server: connect a server that exposes many tools through one connection.
- Analytics and Logs: monitor API calls and debug responses in production.