A chat UI feels simple until you try to ship the details: streaming text, images in the user message, tool calls, citations, resumable sessions, and a clean fallback for non-streaming clients. The AI Chat v2 endpoint gives you one API surface for that whole shape instead of forcing you to glue several patterns together.
This guide walks through the practical integration path: start with a normal JSON response, add server-side conversation state, then move to NDJSON or SSE streaming so your frontend can render tokens, tool activity, and final usage separately.
What you can do
The endpoint is:
POST https://api.acedata.cloud/aichat2/conversations
You authenticate with:
authorization: Bearer {token}
For the simplest request, send model and question. The response comes back as { answer, id }, which makes it compatible with a conventional request/response chat implementation.
AI Chat v2 also supports more advanced patterns from the same endpoint:
-
stateful: truefor server-side multi-turn conversation state -
idto continue a previous conversation -
accept: application/x-ndjsonfor server or CLI streaming -
accept: text/event-streamfor browser-style SSE streaming -
message[]blocks for text, image URLs, and file URLs - structured stream event types such as
text_delta,thinking,tool_use,tool_result,card,citation,artifact,ask_user_question,error, anddone -
async: truefor background execution -
action: retrievewithidto query an async or saved conversation result
The model list is broad, but the examples in the documentation use gpt-5.4, so we will keep the examples here on that model.
Start with a plain JSON request
Before adding streaming, make sure the basic call works. This is the smallest useful version:
curl -X POST 'https://api.acedata.cloud/aichat2/conversations' \
-H 'accept: application/json' \
-H 'authorization: Bearer {token}' \
-H 'content-type: application/json' \
-d '{
"model": "gpt-5.4",
"question": "Introduce AceDataCloud in one sentence."
}'
A JSON response contains an answer and an id:
{
"answer": "AceDataCloud is a unified API platform that aggregates mainstream AI models and multimodal services, allowing developers to access services like GPT, Claude, Gemini, Midjourney, Suno, Veo, etc., with a single key.",
"id": "f2f4b3e8-0c0a-4d3a-aaa2-7ff80c0a1c44"
}
This mode is the right first milestone because you can connect it to an existing backend route, verify authentication, and store the returned id before you worry about stream parsing.
Keep conversation state on the server
For multi-turn chat, pass stateful: true. The endpoint returns an id; later requests can bring the same id back instead of resending the entire message history.
First request:
curl -X POST 'https://api.acedata.cloud/aichat2/conversations' \
-H 'accept: application/json' \
-H 'authorization: Bearer {token}' \
-H 'content-type: application/json' \
-d '{
"model": "gpt-5.4",
"stateful": true,
"question": "Remember a number: 42."
}'
Follow-up request:
curl -X POST 'https://api.acedata.cloud/aichat2/conversations' \
-H 'accept: application/json' \
-H 'authorization: Bearer {token}' \
-H 'content-type: application/json' \
-d '{
"model": "gpt-5.4",
"stateful": true,
"id": "f2f4b3e8-0c0a-4d3a-aaa2-7ff80c0a1c44",
"question": "What number did I just ask you to remember?"
}'
The documentation notes that stateful defaults to true. If you do not want the server to save a round, explicitly pass stateful: false.
Stream NDJSON on the server
For backends, CLIs, and Node-style stream parsers, NDJSON is often simpler than SSE: each line is one JSON object. Set the accept header to application/x-ndjson.
import json
import requests
url = "https://api.acedata.cloud/aichat2/conversations"
headers = {
"accept": "application/x-ndjson",
"authorization": "Bearer {token}",
"content-type": "application/json",
}
payload = {
"model": "gpt-5.4",
"stateful": True,
"question": "Introduce Hangzhou in three sentences.",
}
with requests.post(url, json=payload, headers=headers, stream=True) as resp:
answer = ""
for line in resp.iter_lines():
if not line:
continue
event = json.loads(line)
if event.get("type") == "text_delta":
answer += event["content"]
print(event["delta_answer"], end="", flush=True)
elif event.get("type") == "done":
print()
print("usage =", event.get("usage"))
The key event is text_delta. Concatenating every text_delta.content gives you the same final answer you would have received in application/json mode. The final done event can include usage with token counts and terminal_reason.
Render tool activity without blocking the answer
AI Chat v2 can emit structured events for tool use. For example, the stream can include:
{"type":"tool_use","tool_id":"toolu_01ABCDEF","tool_name":"web_search","input":{"query":"上海 2026 春季展览"},"id":"f2f4b3e8-..."}
{"type":"tool_result","tool_id":"toolu_01ABCDEF","output":"...","is_error":false,"id":"f2f4b3e8-..."}
{"type":"text_delta","content":"目前","delta_answer":"目前","id":"f2f4b3e8-..."}
A good UI treats these as separate channels. Render text_delta into the assistant message, show tool_use and tool_result as a collapsible activity log, and attach citation or card events where they belong. If your product does not expose tool details, you can ignore tool_use, tool_result, card, and citation; the final answer still streams through text_delta.
You can also limit self-invocation with max_turns. Setting a low value such as max_turns: 1 enforces a single response without allowing tool invocation.
Add images and files with message blocks
When a user sends more than plain text, use message instead of question. Each item is a content block.
{
"model": "gpt-5.4",
"stateful": true,
"message": [
{ "type": "text", "text": "How many cats are in this picture?" },
{ "type": "image_url", "image_url": { "url": "https://cdn.acedata.cloud/cats.jpg" } }
]
}
Supported block types are text, image_url, and file_url. For older clients, the v2 endpoint still recognizes references: ["https://...", ...]; image suffixes become image_url blocks and other file types become file_url blocks.
When to use async mode
If the request comes from a webhook, monitoring alert, or other background job, pass async: true. The endpoint can return a task-style response with task_id, conversation_id, id, and status: queued. You can later query the result with action: retrieve and the same id, or provide a callback_url that receives { status, answer, usage, error } when the task completes.
That gives you one integration path for interactive chat and unattended jobs, while keeping the request body shape familiar.
The full Ace Data Cloud reference, including event tables and resume flow details, is here: https://platform.acedata.cloud/documents/aichat2-conversations-integration
Top comments (0)