Build Agentic Apps with AceDataCloud AI Chat v2
A chat endpoint can return text. An agent endpoint can inspect a file, search the web, call an authorized tool, ask for missing information, and stream every step back to your interface.
That is the practical difference behind AceDataCloud AI Chat v2. The endpoint at POST https://api.acedata.cloud/aichat2/conversations preserves the simple request style of the original conversation API while adding multimodal content blocks, autonomous multi-round tool use, MCP connections, structured streaming events, resumable conversations, and conversation CRUD actions.

This guide focuses on the integration decisions that matter when moving from a basic chatbot to a production agent experience.
¶ What changes in AI Chat v2?
AI Chat v2 is a comprehensive upgrade of the original /aichat/conversations interface. Its main capabilities are:
- Multimodal input: send ordered
text,image_url, andfile_urlblocks in onemessagearray. - Autonomous tool use: the model can call built-in tools and user-authorized MCP servers across multiple rounds.
- Typed streaming: render text, tool activity, citations, cards, artifacts, errors, and completion data as separate events.
- Pause and resume: handle an
ask_user_questionevent and continue the same conversation with a tool result. - Conversation management: retrieve, batch-retrieve, update, and delete through actions on the same endpoint.
- Backward-compatible requests: existing
model+questionclients can migrate by changing the endpoint path.
The important architectural shift is that your frontend no longer has to treat the response as one opaque string. It can become an event-driven workspace.
¶ Start with the backward-compatible path
The lowest-risk migration is intentionally small. Keep the existing request body and change only the endpoint:
curl -X POST 'https://api.acedata.cloud/aichat2/conversations' \
-H 'accept: application/json' \
-H 'authorization: Bearer YOUR_API_TOKEN' \
-H 'content-type: application/json' \
-d '{
"model": "YOUR_SUPPORTED_MODEL",
"question": "Summarize the three most important points in this request.",
"stateful": true
}'
In JSON mode, the response retains the familiar shape:
{
"answer": "...",
"id": "conversation-uuid"
}
Keep the returned id. Sending it in the next request continues the managed conversation without requiring your client to resend the full message history.
statefuldefaults totrue. Set it tofalseexplicitly when a round should not be stored.
This first migration step lets you validate authentication, model availability, latency, and application behavior before introducing tools or a new streaming UI.
¶ Use message when order and modality matter
The older question field is convenient for text-only prompts. For text mixed with images or files, use an ordered message array:
{
"model": "YOUR_SUPPORTED_MODEL",
"stateful": true,
"message": [
{
"type": "text",
"text": "Read the attached report and compare its chart with this screenshot. Return five findings."
},
{
"type": "file_url",
"file_url": {
"url": "https://example.com/report.pdf"
}
},
{
"type": "image_url",
"image_url": {
"url": "https://example.com/dashboard.png"
}
}
]
}
Supported content blocks are:
| Block | Required value | Best use |
|---|---|---|
text |
text |
Instructions, context, and questions |
image_url |
image_url.url |
Screenshots, charts, product images |
file_url |
file_url.url |
PDF, CSV, TXT, and other files |
AI Chat v2 still recognizes the older references array. Image extensions are converted to image blocks, while other URLs become file blocks. That compatibility is useful during migration, but message is the better long-term contract when content order matters.
¶ Design the client around events, not just tokens
Choose the response format with the accept header:
| Client | Header | Format |
|---|---|---|
| Browser UI | text/event-stream |
Server-Sent Events |
| Node, Python, CLI, worker | application/x-ndjson |
One JSON event per line |
| Simple backend request | application/json |
Final {answer, id} object |
A minimal Python NDJSON consumer can assemble text while preserving other event types:
import json
import os
import requests
url = "https://api.acedata.cloud/aichat2/conversations"
headers = {
"accept": "application/x-ndjson",
"authorization": f"Bearer {os.environ['ACEDATA_API_TOKEN']}",
"content-type": "application/json",
}
payload = {
"model": os.environ["ACEDATA_CHAT_MODEL"],
"question": "Research the topic, cite sources, and return a concise brief.",
"stateful": True,
}
with requests.post(url, json=payload, headers=headers, stream=True, timeout=120) as response:
response.raise_for_status()
answer = []
for raw_line in response.iter_lines():
if not raw_line:
continue
event = json.loads(raw_line)
event_type = event.get("type")
if event_type == "text_delta":
answer.append(event.get("content", ""))
elif event_type == "tool_use":
print("tool started:", event.get("tool_name"))
elif event_type == "tool_result":
print("tool finished; error =", event.get("is_error"))
elif event_type == "citation":
print("source:", event)
elif event_type == "artifact":
print("artifact:", event)
elif event_type == "error":
raise RuntimeError(event.get("message", "unknown agent error"))
elif event_type == "done":
print("usage:", event.get("usage"))
print("terminal reason:", event.get("terminal_reason"))
print("".join(answer))
Production interfaces should define a renderer or handler for each event family:
text_deltaupdates the answer region.tool_useandtool_resultdrive activity states and audit views.citationattaches sources to relevant output.cardrenders structured previews.artifactexposes a separate generated deliverable.thinkingshould be handled only when the chosen model exposes it.errorterminates or recovers the current round.donecloses the stream and records token usage plus the terminal reason.
If your interface only needs the final answer, concatenate content from every text_delta. That is equivalent to the answer returned in JSON mode.
¶ Let the agent use tools—but make the activity visible
Tool calling is enabled by default. AI Chat v2 can use built-in capabilities such as web search, page extraction, and file reading. It can also mount user-authorized MCP connections such as Google Drive, Notion, Slack, or GitHub.
The stream exposes the boundary clearly:
{"type":"tool_use","tool_id":"toolu_01","tool_name":"web_search","input":{"query":"topic"}}
{"type":"tool_result","tool_id":"toolu_01","output":"...","is_error":false}
Pair tool_use and tool_result by tool_id. This supports progress indicators, latency measurements, error handling, and an operator-readable audit trail.
Use max_turns to constrain how many autonomous tool rounds can occur in one request. The accepted range is 1–500; the documented default is 500. A lower value reduces open-ended execution. Setting max_turns: 1 forces a single response and prevents tool calls.
A practical rollout pattern is:
- Begin in JSON mode without tools in the product UI.
- Add NDJSON or SSE and render
text_deltaplusdone. - Surface tool activity as a collapsed timeline.
- Add citations and artifact rendering.
- Set a deliberate
max_turnsbased on task complexity and cost tolerance. - Enable write-capable connections only after reviewing their confirmation model.
¶ Handle questions as a resumable state machine
An agent sometimes needs clarification. In that case, the stream can emit ask_user_question, and the conversation enters an awaiting_user_input state.
Your client should:
- Save the conversation
idandtool_id. - Render the question and its options.
- Collect the user’s response.
- Send another request with the same conversation
id. - Return the answer through
tool_resultsfor the pending tool call.
This is more robust than pretending the agent can finish with missing information. It also makes approval gates possible for sensitive actions.
¶ Separate interactive and unattended execution
For webhooks, CI/CD jobs, and monitoring systems, async: true returns a task identifier while the work continues. The result can be retrieved later or delivered to an HTTP(S) callback URL.
Unattended write access should be explicit. AI Chat v2 supports allowed_skills, allowed_mcp_servers, or an equivalent unattended_policy object. An empty list authorizes no capabilities. Pre-authorization does not bypass a connector’s own security mechanism; a skill that does not support unattended confirmation should still dry-run or refuse the write.
That distinction matters: connection availability is not the same as permission to act without a person present.
¶ Production checklist
Before launching an agentic interface, verify:
- API credentials are stored server-side, never in browser code.
- Model names come from the current supported model list rather than hard-coded assumptions.
- The stream parser tolerates partial network chunks and unknown event types.
- Tool events are paired by
tool_idand errors are recorded. -
max_turnsis set intentionally. - Citation and artifact URLs are rendered safely.
-
ask_user_questionpersists enough state to resume later. - Write-capable connections retain confirmation or narrowly scoped unattended authorization.
-
done.usageandterminal_reasonfeed observability. - Conversation deletion and retention policies match your product requirements.
¶ The shortest path from chatbot to agent
AI Chat v2 does not force a full rewrite. Start by changing the endpoint and preserving your current request body. Then move to multimodal message blocks, adopt structured streaming, expose tool progress, and add pause-and-resume handling.
The result is a client that can grow from simple answers into an auditable agent workflow—without inventing a separate protocol for files, tools, citations, artifacts, or user clarification.

