Task API
Open in CMSThe Task API lets your own systems use an agent as a single-task worker. Your server sends one input over REST with an API key, the agent does the task, and the full result comes back in the HTTP response. There is no streaming, no conversation, and no follow-up: every call is a new, independent task.
Use it when a system needs your agent's judgement, knowledge, and tools for one job at a time, for example:
- Classifying tickets or emails. Your helpdesk sends each new ticket and gets back a category, a priority, and a summary as JSON it can store directly.
- Extracting data from documents. Your back office sends an invoice or a report as an attachment and gets back the fields it needs.
- Writing a draft. Your CRM sends a customer's details and gets back a follow-up email in Markdown, written with the agent's knowledge of your products.
The agent works with its own main instructions, knowledge, and tools, so what you set up for chat is used here too. On top of that, the Task API has its own settings: task instructions added to every call, and the format the result must have (Markdown, plain text, JSON, or JSON that matches your schema).
In the Teach Your Agent example, Atlas holds Kirana Group's SOPs. Kirana's facility team logs maintenance tickets in its own system. Each new ticket is sent to Atlas's Task API, which returns a category, a priority, and a one-line summary based on Kirana's maintenance SOP, so the ticket lands with the right team.
Before you start
- Service agents only. The Task API is available for service agents. A personal agent cannot switch it on. See Personal vs Service Agents if you are not sure which kind your agent is.
- Who can turn it on. Agent contributors and organization Owners and Admins.
- Off by default. Every agent starts with the API switched off. Nothing can call it until someone switches it on and creates a key.
- Call it from a server. The key gives full access to the agent's tasks. Keep it on your back end and never call the API from a browser page or a mobile app.
How the agent behaves in a task
In a task, the agent works in task mode:
- It does the task in one pass and returns only the result.
- It does not greet, ask follow-up questions, or offer more help.
- It does not show interactive widgets or canvases.
- When information it needs is missing, it says in the result what is missing, instead of asking for it.
The agent's main instructions, knowledge, and tools are used as configured, unless you switch them off in the settings. Each task is its own conversation: the agent does not remember earlier tasks.
Turn on the API and create a key
In the CMS, open Channels β Task API in your agent's sidebar. The page has three tabs: Access, Settings, and Test.
- On the Access tab, switch on the API. The page shows the endpoint for this agent and code samples.
- The agent has two key slots, Primary and Secondary. In one of them, create a key and choose an Expiry: 7, 14, 30, 90, 180, or 365 days, or never. After that date the key stops working.
- Create the key and copy it right away. The full key (it starts with
qta_) is shown only once. Afterwards the page shows only its last characters and the expiry date. - Store the key in your server's secret store (for example a vault or an environment variable on the server that calls the API).
Replace or revoke a key
- Lost a key? You cannot view it again. Create a new key in the same slot; the old key stops working.
- Revoke a key to stop it, for example when it may have leaked or a system is retired. A revoked key cannot be turned back on.
- Switch the API off to stop all keys of the agent at once.
Revoking a key or switching the API off takes effect within seconds.
Rotate a key without downtime
The two slots exist so you can change keys without interrupting your system:
- Your system uses the Primary key.
- Create a key in the Secondary slot.
- Move your system to the Secondary key and check that its tasks succeed.
- Revoke the Primary key.
Next time, do the same in the other direction.
Task settings
The Settings tab controls how the agent handles tasks. Settings apply to every call made with this agent's keys.
| Setting | What it does |
|---|---|
| Additional prompt | Task instructions added to every call, up to 20,000 characters. When they conflict with the agent's other instructions, the additional prompt wins. Write here what the task is (for example "Classify the maintenance ticket below"); the request's input is the data the task works on. |
| Response format | The shape of the result: Markdown (default), Plain text, JSON, or JSON schema. See Response formats. |
| Include personality | Adds the agent's personality to its instructions. Off by default, because a task usually needs a plain result. |
| Include interaction style | Adds the agent's interaction style. Off by default. |
| Include mission | Adds the agent's mission. On by default. |
| Use knowledge | Lets the agent search its knowledge during the task. On by default. |
| Use tools | Lets the agent use its tools: plugins, MCP servers, custom APIs, web search, and skills. On by default. |
| Keep tasks for | How long task conversations are kept: 7, 30 (default), or 90 days. After that they are deleted. |
These settings apply to new tasks a few seconds after you save; they do not need publishing. The agent itself (its behavior, knowledge and tools) is a different matter: calls to the Task API use the published agent, so changes you make to the agent go live when you click Publish (see Preview and Publish Changes).
Response formats
| Format | What you get |
|---|---|
| Markdown | Text with Markdown formatting in output. |
| Plain text | Text without Markdown in output. |
| JSON | One JSON object. output holds it as text and result holds it parsed. |
| JSON schema | One JSON object that must match the schema you enter. The answer is validated; when it does not match, Qlar asks the agent to repair it once. If it still does not match, the call fails with 422 output_invalid. output holds the object as text and result holds it parsed. |
The schema must have "type": "object" at the root and can be up to 20,000 characters. These keywords are supported:
type:string,number,integer,boolean,object,array,null, or a list of theseproperties,required,additionalProperties(trueorfalse),itemsenumminimumandmaximum,minLengthandmaxLength,minItemsandmaxItemstitleanddescription(use them to explain fields to the agent)
Tip: for the strictest results, list every property in required and set "additionalProperties": false. Not sure how to write a schema? Use Generate from example in the CMS to build one from an example of the JSON you want.
The schema for the Kirana ticket example:
{
"type": "object",
"properties": {
"category": {
"type": "string",
"enum": ["maintenance", "cleaning", "security", "it", "other"],
"description": "The team that should handle the ticket."
},
"priority": {
"type": "string",
"enum": ["low", "medium", "high"],
"description": "high = safety risk or work stopped; low = cosmetic."
},
"summary": {
"type": "string",
"maxLength": 200,
"description": "One sentence describing the problem."
}
},
"required": ["category", "priority", "summary"],
"additionalProperties": false
}
Test a task in the CMS
The Test tab runs a task from the CMS without a key. It uses the settings as they are in the form, even before you save them, on the draft agent. Enter an input and run it to see:
- the output and, for the JSON formats, the parsed result,
- whether the result passed validation,
- the tools the agent called,
- whether a repair was needed.
Use it to tune the additional prompt and the schema before you connect your system.
Request
POST https://api.qlar.ai/dialog/api/task-api/v1/agents/{agentId}/tasks
X-Api-Key: qta_...
Content-Type: application/json
The endpoint, with your agent ID filled in, is shown ready to copy on the Access tab.
Body:
{
"input": "Ticket: the AC in room 3 has been leaking since yesterday.",
"permissions": ["MANAGER"],
"timezone": "Asia/Jakarta"
}
| Field | Required | Description |
|---|---|---|
input | Yes | The data the task works on, up to 100,000 characters. |
permissions | No | Permission codes of the person the task is for, as an array of strings. At most 50 codes, each at most 100 characters. See Permissions. |
attachments | No | Up to 5 files for the agent to read. See Attachments. |
timezone | No | An IANA time zone name, such as Asia/Jakarta. Used for dates and times in the task. Default: UTC. |
The whole request body can be at most 1 MB.
Attachments
Each attachment is an object with a type and a url:
{
"input": "Extract the invoice number, the date, and the total amount.",
"attachments": [
{ "type": "image", "url": "https://example.com/photo.jpg" },
{ "type": "document", "url": "https://example.com/report.pdf", "fileName": "report.pdf" }
]
}
| Field | Required | Description |
|---|---|---|
type | Yes | image or document. |
url | Yes | An https address Qlar can download the file from. It must be publicly reachable: private or internal addresses and redirects are refused. |
fileName | No | The file's name, shown to the agent. |
- At most 5 attachments per task.
- Images up to 10 MB, documents up to 20 MB.
- The same file types as chat uploads, for example JPG, PNG, and WebP images, and PDF, DOCX, XLSX, TXT, and MD documents.
When a file cannot be downloaded or used, the call fails with 400 attachment_invalid.
Response
{
"taskId": "6f1c...",
"status": "completed",
"format": "json_schema",
"output": "{\"category\":\"maintenance\",\"priority\":\"high\"}",
"result": { "category": "maintenance", "priority": "high" },
"durationMs": 6120
}
| Field | Description |
|---|---|
taskId | The ID of the task. Each task is saved as a conversation, so you can find it in the agent's conversation history for audit, until it is deleted after the Keep tasks for period. |
status | completed. |
format | The response format the task used, for example json_schema. |
output | The result as text. |
result | The result parsed as a JSON object. Only for the JSON and JSON schema formats. |
durationMs | How long the task took, in milliseconds. |
Timeouts
A task can take a while, especially when the agent searches its knowledge, calls tools, or reads attachments. Qlar waits up to 180 seconds for a task to finish; after that the call fails with 504 run_timeout. Set your HTTP client's timeout to at least 200 seconds, so your client does not give up before Qlar answers.
Code samples
Each sample sends a task, waits up to 200 seconds, and prints the result. Replace YOUR_AGENT_ID with your agent's ID, and load the key from your secret store instead of qta_your_key.
// Node.js 18 or later (built-in fetch)
const url = "https://api.qlar.ai/dialog/api/task-api/v1/agents/YOUR_AGENT_ID/tasks";
const apiKey = process.env.QLAR_TASK_KEY ?? "qta_your_key";
const response = await fetch(url, {
method: "POST",
headers: {
"X-Api-Key": apiKey,
"Content-Type": "application/json",
},
body: JSON.stringify({
input: "Ticket: the AC in room 3 has been leaking since yesterday.",
permissions: ["MANAGER"],
}),
signal: AbortSignal.timeout(200_000), // at least 200 seconds
});
const data = await response.json();
if (!response.ok) {
console.error(`Task failed (${response.status}): ${data.code} - ${data.message}`);
process.exit(1);
}
console.log("Output:", data.output);
if (data.result) {
console.log("Category:", data.result.category);
}
using System.Text;
using System.Text.Json;
var url = "https://api.qlar.ai/dialog/api/task-api/v1/agents/YOUR_AGENT_ID/tasks";
var apiKey = Environment.GetEnvironmentVariable("QLAR_TASK_KEY") ?? "qta_your_key";
using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(200) }; // at least 200 seconds
http.DefaultRequestHeaders.Add("X-Api-Key", apiKey);
var body = """
{
"input": "Ticket: the AC in room 3 has been leaking since yesterday.",
"permissions": ["MANAGER"]
}
""";
var response = await http.PostAsync(url, new StringContent(body, Encoding.UTF8, "application/json"));
using var doc = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
var root = doc.RootElement;
if (!response.IsSuccessStatusCode)
{
Console.Error.WriteLine(
$"Task failed ({(int)response.StatusCode}): {root.GetProperty("code")} - {root.GetProperty("message")}");
return;
}
Console.WriteLine($"Output: {root.GetProperty("output").GetString()}");
if (root.TryGetProperty("result", out var result) && result.ValueKind == JsonValueKind.Object)
{
Console.WriteLine($"Category: {result.GetProperty("category").GetString()}");
}
import os
import sys
import requests
url = "https://api.qlar.ai/dialog/api/task-api/v1/agents/YOUR_AGENT_ID/tasks"
api_key = os.environ.get("QLAR_TASK_KEY", "qta_your_key")
response = requests.post(
url,
headers={"X-Api-Key": api_key},
json={
"input": "Ticket: the AC in room 3 has been leaking since yesterday.",
"permissions": ["MANAGER"],
},
timeout=200, # at least 200 seconds
)
data = response.json()
if not response.ok:
print(f"Task failed ({response.status_code}): {data.get('code')} - {data.get('message')}", file=sys.stderr)
sys.exit(1)
print("Output:", data["output"])
if data.get("result"):
print("Category:", data["result"]["category"])
// Java 11 or later (java.net.http). Text blocks need Java 15 or later.
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class QlarTask {
public static void main(String[] args) throws Exception {
String url = "https://api.qlar.ai/dialog/api/task-api/v1/agents/YOUR_AGENT_ID/tasks";
String apiKey = System.getenv().getOrDefault("QLAR_TASK_KEY", "qta_your_key");
String body = """
{
"input": "Ticket: the AC in room 3 has been leaking since yesterday.",
"permissions": ["MANAGER"]
}
""";
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.build();
HttpRequest request = HttpRequest.newBuilder(URI.create(url))
.timeout(Duration.ofSeconds(200)) // at least 200 seconds
.header("X-Api-Key", apiKey)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() / 100 != 2) {
// The body is {"code": "...", "message": "..."}; parse it with your JSON library.
System.err.println("Task failed (" + response.statusCode() + "): " + response.body());
System.exit(1);
}
// The body holds taskId, output and, for the JSON formats, result.
// Parse it with your JSON library (for example Jackson or Gson).
System.out.println(response.body());
}
}
With the JSON schema format and the schema above, the Kirana ticket returns:
{
"taskId": "6f1c...",
"status": "completed",
"format": "json_schema",
"output": "{\"category\":\"maintenance\",\"priority\":\"high\",\"summary\":\"The AC in room 3 has been leaking since yesterday.\"}",
"result": {
"category": "maintenance",
"priority": "high",
"summary": "The AC in room 3 has been leaking since yesterday."
},
"durationMs": 6120
}
Permissions
Permission codes work the same way as in the agent's conversations: knowledge without a code can be used in every task, and knowledge with codes is used only when the task carries one of them. The codes are also passed to the agent's custom API tools, so your own APIs can check them. See Knowledge Permissions.
The caller is responsible for its codes. Qlar does not know who the person behind a task is: it trusts the codes the key holder sends. Anyone with the key can send any code, including codes for confidential knowledge. Your server must decide which codes each task carries, for example by mapping your own roles or groups to permission codes. Never take codes from a user's browser as they are.
Billing
The Task API is billed like chat:
- The AI usage (tokens) of each task is charged to your organization's credit, the same as a chat answer from the same agent. Tools such as web search are billed as usual.
- When the agent runs on your organization's own AI model, that usage is free of Qlar credits, as with chat. See AI Models.
- A repair pass for the JSON schema format uses tokens too.
- Tasks run from the Test tab are billed like Preview chats.
When the organization is out of credit, calls fail with 402 credit_exhausted. See How Credits Work.
Rate limits
The Task API has no rate limits yet. They are coming; until then, keep the number of tasks you send at the same time reasonable.
Errors
Errors use the HTTP status code and a JSON body with a machine-readable code and a human-readable message. When the task had already started, the body also has its taskId.
{
"code": "invalid_request",
"message": "input is required."
}
Build your logic on code. The message can change and is meant for people.
| HTTP status | code | Meaning |
|---|---|---|
| 400 | invalid_request | The body is not valid, for example a missing or too long input, or too many permission codes or attachments. |
| 400 | attachment_invalid | An attachment could not be downloaded or used: not https, not publicly reachable, too large, or an unsupported file type. |
| 401 | unauthorized | The key is missing, wrong, expired, or revoked, the API is switched off, or the agent is not a service agent. All these cases get the same answer on purpose. |
| 402 | credit_exhausted | The organization is out of credit. Top up, then try again. |
| 403 | agent_limit_reached | The agent has used up the monthly usage limit that the organization's admins set for it. |
| 403 | own_model_required | The organization only allows its own AI models, and the agent has none. See Only Use Your Own Models. |
| 413 | The request body is larger than 1 MB. | |
| 422 | output_invalid | The answer did not match the JSON format, even after one automatic repair. The body also has output (the raw answer) and errors (what did not match). |
| 504 | run_timeout | The task did not finish within 180 seconds. The body has the taskId. |
Security best practices
- Keep the key on a server. Call the API only from back-end code. Never put a key in a browser page, a mobile app, a desktop app you distribute, or a public repository: anyone who can read the code can use the key.
- Decide permissions on your server. Send only the codes the person behind the task is entitled to.
- Store the key as a secret. Use your vault or your platform's secret variables, not a config file checked into source control. Do not print the key in logs.
- Set an expiry and rotate using the two slots, as described in Rotate a key without downtime.
- Revoke at once on a leak. If a key may have been exposed, revoke it on the Access tab and create a new one.
- Treat inputs and attachments as data. Anything you put in
inputor an attachment is read by the agent. Put the instructions in the Additional prompt, not in text that comes from outside your organization. - Switch the API off when you no longer use it.
Troubleshooting
| Symptom | What to check |
|---|---|
Every call returns 401 unauthorized | Check that the API is switched on, that the key is the full value starting with qta_, that it is sent in the X-Api-Key header, that it has not expired or been revoked (or replaced by a newer key in the same slot), and that the agent ID in the URL matches the agent of the key. The agent must also still be a service agent. |
| My changes have no effect | Task API settings apply a few seconds after you save. Changes to the agent itself (behavior, knowledge, tools) go live only when you click Publish. The Test tab runs the draft agent with the form as it is, so it can differ from live calls until you save and publish. |
422 output_invalid | The schema may be hard to satisfy, or the additional prompt may ask for something the schema does not allow. Read errors in the response, try the input on the Test tab, and simplify the schema or make the instructions clearer. |
504 run_timeout | The task took longer than 180 seconds. Shorten the input, reduce attachments, or switch off tools the task does not need. |
| My client gives up before an answer arrives | Raise your HTTP client's timeout to at least 200 seconds. |
400 attachment_invalid | Check that the URL is https, opens without a login or redirect from the public internet, and that the file is within the size limit and of a supported type. |
| The result asks a question or says information is missing | In task mode the agent cannot ask back. Put the missing details in input, or describe in the Additional prompt what to do when something is missing. |
| The agent does not use confidential knowledge | The task does not carry the knowledge's permission code. Send it in permissions. |
Related pages
- Knowledge Permissions: the permission codes tasks carry.
- Preview and Publish Changes: make settings changes live.
- Knowledge Search API: get matching chunks of the agent's knowledge instead of a finished result.
- Let Other Agents Call Yours: let other agents talk to your agent.
- How Credits Work
- Field-by-field reference: Task API