Version: MVP / API v1
Default localbase URL: http://localhost:18082
Content type unless otherwise stated: application/json
sequenceDiagram
participant UI as Client
participant C as Coordinator
participant DB as MySQL
participant AC as AgentCore
participant OS as MinIO
UI->>C: POST /api/v1/projects/{id}/tasks
C-->>UI: taskId + stable business sessionId
UI->>C: POST /api/v1/projects/{id}/tasks/{taskId}/messages
C->>DB: Persist message and dispatch
C-->>UI: 202 ACCEPTED
UI->>C: GET /api/v1/projects/{id}/tasks/{taskId}/events
C-->>UI: Project SSE events
C->>AC: Submit coordinator run
AC-->>C: sessionId
C->>DB: Persist session and cursor
C->>AC: streamEvents(afterSequence)
AC-->>C: CoordinatorDecision
C->>AC: Submit expert run(s)
AC-->>C: sessionId
C->>AC: streamEvents(afterSequence)
AC-->>C: Progress and result events
C->>OS: Read input / register output
C->>DB: Persist state and public events
C-->>UI: SSE progress and final response
API groups:
| Group | Prefix | Purpose |
|---|---|---|
| Business API | /api/v1 |
Projects, messages, events, tasks, human input and artifacts |
| Health API | /health, /ready, /actuator |
Runtime probes |
| Local mock API | /mock |
Local AgentCore, expert registry and file-store simulation |
| Remote AgentCore contract | Configurable | Outbound protocol used by Coordinator |
Project
-> Task (one conversation)
-> one stable business sessionId
-> many messages
-> many Coordinator/expert runs
-> one isolated event stream
The Task's business sessionId is generated by Coordinator when a new
conversation is created and remains unchanged for that conversation. It is
Coordinator-owned context and is distinct from the AgentCore session ID.
AgentCore also returns a run sessionId for each individual Coordinator or
expert run. Run session IDs are not shared and are only used to address the
corresponding AgentCore run.
All /api/v1/** endpoints require:
| Header | Required | Description |
|---|---|---|
X-Tenant-Id |
Yes | Tenant boundary |
X-User-Id |
Yes | Current user identity |
Content-Type |
For JSON body | application/json |
Missing identity headers return 401 IDENTITY_REQUIRED.
Example:
X-Tenant-Id: tenant-001
X-User-Id: user-001
Content-Type: application/json- Timestamps are ISO-8601 strings, for example
2026-08-03T18:27:21.576+08:00. - Public request fields explicitly shown with underscores use
snake_case. - Most response models use Java/Jackson
camelCase. - IDs are opaque strings. Clients must not derive meaning from their prefixes.
{
"code": "PROJECT_NOT_FOUND",
"message": "Project was not found.",
"time": "2026-08-03T10:00:00Z"
}Common HTTP statuses:
| Status | Meaning |
|---|---|
400 |
Validation failure or invalid operation |
401 |
Identity headers missing |
403 |
User lacks the required project role |
404 |
Resource not found, or hidden by tenant/project boundary |
409 |
Resource state conflict or idempotency conflict |
503 |
MVP disabled or emergency stop enabled |
| Role | Typical permissions |
|---|---|
OWNER |
Project administration, members, experts, task initiation |
MEMBER |
Project access and collaboration |
VIEWER |
Read access |
POST /api/v1/projects
Request:
{
"name": "Risk Review",
"description": "Analyze service release risks"
}Constraints:
name: required, maximum 128 characters.description: optional, maximum 1024 characters.
Response: 201 Created
{
"id": "project-...",
"name": "Risk Review",
"description": "Analyze service release risks",
"status": "ACTIVE",
"createdAt": "2026-08-03T10:00:00Z",
"updatedAt": "2026-08-03T10:00:00Z",
"members": [
{
"userId": "user-001",
"role": "OWNER"
}
],
"experts": []
}GET /api/v1/projects/{projectId}
Response: 200 OK, body is ProjectView.
PATCH /api/v1/projects/{projectId}
All fields are optional:
{
"name": "Updated name",
"description": "Updated description",
"status": "ARCHIVED"
}status values: ACTIVE, ARCHIVED.
Response: 200 OK, body is the updated ProjectView.
POST /api/v1/projects/{projectId}/members
{
"userId": "user-002",
"role": "MEMBER"
}Response: 200 OK, body is the updated ProjectView.
DELETE /api/v1/projects/{projectId}/members/{userId}
Response: 204 No Content.
POST /api/v1/projects/{projectId}/experts
{
"expertId": "analysis-expert",
"enabled": true
}Response: 200 OK, body is the updated ProjectView.
DELETE /api/v1/projects/{projectId}/experts/{expertId}
Response: 204 No Content.
POST /api/v1/projects/{projectId}/tasks
Every new conversation must call this endpoint once.
{
"title": "Payment API risk review"
}Response: 201 Created
{
"taskId": "task-...",
"projectId": "project-...",
"sessionId": "session-...",
"title": "Payment API risk review",
"status": "ACTIVE",
"createdAt": "2026-08-03T10:00:00Z"
}GET /api/v1/projects/{projectId}/tasks/{taskId}
Response: 200 OK, body is ConversationTaskView.
POST /api/v1/projects/{projectId}/tasks/{taskId}/messages
This is the primary asynchronous entry point. The request is persisted before the response is returned.
{
"client_message_id": "client-msg-001",
"text": "分析接口风险并撰写报告",
"attachment_refs": ["artifact-..."]
}Constraints:
| Field | Required | Constraint |
|---|---|---|
client_message_id |
Yes | Maximum 128 characters |
text |
Yes | Maximum 10,000 characters |
attachment_refs |
No | Maximum 10 entries |
Response: 202 Accepted
{
"messageId": "message-...",
"taskId": "task-...",
"sessionId": "session-...",
"status": "ACCEPTED"
}client_message_id is generated by the caller and uniquely identifies one
message inside a conversation task. Retrying with the same value returns the
existing accepted message, so a second message-level idempotency key is not
required.
Database tables use id BIGINT AUTO_INCREMENT as an internal, meaningless
primary key. Domain entities also have a stable business identity:
- Aggregate and event tables use a unique
business_idcolumn. - Association tables use their natural business column combination as a unique constraint.
- API fields such as
id,taskId,messageId, andartifactIdexpose the business identity and never expose the numeric database primary key. - Cross-table domain references point to business IDs. The numeric key remains an implementation detail for storage, indexing, and deterministic ordering.
GET /api/v1/projects/{projectId}/tasks/{taskId}/events
Request headers:
Accept: text/event-stream
Last-Event-ID: 42Last-Event-ID is optional. When supplied, events with a greater sequence are
replayed from MySQL before live delivery. Invalid values are treated as 0.
Response content type: text/event-stream
id: 43
event: TASK_STARTED
data: {"id":"event-...","projectId":"project-...","taskId":"task-...","messageId":"message-...","sequence":43,"type":"TASK_STARTED","payload":{"messageId":"message-...","text":"Expert analysis-expert accepted the task."},"createdAt":"2026-08-03T10:00:00Z"}
Public event types:
| Type | Meaning |
|---|---|
COORDINATOR_ANALYZING |
Coordinator accepted the message for analysis |
PLAN_CREATED |
A plan was created |
PLAN_REVISED |
A replacement plan version was created |
TASK_STARTED |
Expert accepted a task |
TASK_PROGRESS_UPDATED |
Expert progress changed |
TASK_WAITING_HUMAN |
Human input is required |
TASK_SUCCEEDED |
Expert task completed |
TASK_FAILED |
Expert task failed |
ARTIFACT_CREATED |
Output artifact was registered |
FINAL_RESPONSE |
Final user-facing response |
Standard event payload:
{
"messageId": "message-...",
"text": "Human-readable event text"
}Human-input event payload:
{
"messageId": "message-...",
"humanRequestId": "human-...",
"requestType": "CLARIFICATION",
"text": "Please provide the missing information."
}The internal type MESSAGE_ACCEPTED_INTERNAL is not delivered through the
public SSE stream.
POST /api/v1/projects/{projectId}/intent-analysis
This endpoint is intended for diagnostics and direct analysis. The normal product workflow should use the asynchronous message endpoint.
{
"text": "分析附件并生成报告",
"attachment_refs": ["artifact-..."]
}Response variants:
Direct answer:
{
"decision_type": "ANSWER",
"answer": "Answer text",
"analysis_id": "analysis-..."
}Ask human:
{
"decision_type": "ASK_HUMAN",
"question": "请补充要处理的具体对象、目标或期望输出。",
"analysis_id": "analysis-...",
"human_request_id": "human-..."
}Create plan:
{
"decision_type": "CREATE_PLAN",
"task_intent": {
"intent": "ANALYZE",
"objective": "分析附件并生成报告",
"expected_outputs": ["分析报告"],
"constraints": ["使用项目当前可用专家和已有上下文"],
"required_capabilities": ["analysis", "writing"],
"input_refs": ["artifact-..."],
"missing_information": [],
"risk_level": "MEDIUM",
"execution_mode": "MULTI_EXPERT"
},
"analysis_id": "analysis-..."
}Enums:
decision_type:ANSWER,ASK_HUMAN,CREATE_PLANrisk_level:LOW,MEDIUM,HIGHexecution_mode:SINGLE_EXPERT,MULTI_EXPERT
POST /api/v1/projects/{projectId}/human-requests/{requestId}/responses
{
"decision": "ANSWER",
"response": {
"text": "Use the payment API as the analysis target."
},
"idempotencyKey": "human-response-001"
}decision values:
ANSWER: provide clarification.APPROVE: approve a proposed action.REJECT: reject a proposed action.
Response: 200 OK
{
"id": "human-...",
"projectId": "project-...",
"taskId": "task-...",
"requestType": "CLARIFICATION",
"question": "Which API should be analyzed?",
"status": "RESOLVED",
"decision": "ANSWER",
"response": {
"text": "Use the payment API as the analysis target."
},
"expiresAt": "2026-08-04T10:00:00Z"
}requestType values: CLARIFICATION, APPROVAL.
DELETE /api/v1/projects/{projectId}/expert-tasks/{expertTaskId}
The Coordinator forwards cancellation to AgentCore and persists the returned terminal event.
Response: 200 OK
{
"id": "task-...",
"planId": "plan-...",
"taskKey": "analyze-risk",
"requestId": "message-...:analyze-risk",
"expertId": "analysis-expert",
"sessionId": "run-...",
"status": "CANCELLED",
"objective": "Analyze API risks",
"expectedOutput": "Risk analysis",
"acceptanceCriteria": "Result is non-empty",
"resultJson": null,
"correctionOf": null,
"correctionCount": 0,
"dependencies": [],
"requiredCapabilities": ["analysis"],
"lastSequence": 4
}POST /api/v1/projects/{projectId}/artifacts/uploads
{
"fileName": "requirements.txt",
"mediaType": "text/plain",
"taskId": null
}Response: 200 OK
{
"artifactId": "artifact-...",
"version": 1,
"fileName": "requirements.txt",
"mediaType": "text/plain",
"size": null,
"sha256": null,
"status": "UPLOADING",
"uploadUrl": "http://...",
"downloadUrl": "http://..."
}Upload the bytes to uploadUrl, then call the completion endpoint.
POST /api/v1/projects/{projectId}/artifacts/{artifactId}/complete
Response: 200 OK, body is ArtifactView with status AVAILABLE, size and
SHA-256 populated.
Calling completion before the object exists returns 409 Conflict.
GET /api/v1/projects/{projectId}/artifacts/{artifactId}
Response: 200 OK, body is ArtifactView.
Tool name: upload_artifact
POST /api/v1/agent-tools/projects/{projectId}/tasks/{taskId}/artifacts
AgentCore must register this endpoint as a file-capable HTTP tool. The Agent
only sees the tool name and its file argument. AgentCore sends the generated
file as multipart/form-data and injects these headers:
Expert task submission declares the tool and supplies routing metadata:
{
"requiredTools": ["upload_artifact"],
"toolContext": {
"projectId": "project-...",
"taskId": "task-..."
}
}toolContext is AgentCore runtime metadata used to resolve the registered URL
template. It must not be exposed as model-controlled tool arguments.
| Header | Meaning |
|---|---|
X-AgentCore-Tool-Token |
Coordinator-configured tool credential |
X-Session-Id |
Stable business session for the conversation Task |
X-Agent-Run-Id |
AgentCore run sessionId returned by task submission |
X-Agent-Id |
Expert Agent identifier assigned to the run |
Multipart field:
| Field | Type | Required | Meaning |
|---|---|---|---|
file |
binary file | yes | File generated by the Agent |
Response: 201 Created
{
"artifactId": "artifact-...",
"version": 1,
"fileName": "analysis.md",
"mediaType": "text/markdown",
"size": 1024,
"sha256": "...",
"status": "AVAILABLE",
"downloadUrl": "https://..."
}Coordinator validates that the Agent run belongs to the supplied Project, conversation Task, business session and Agent. It then writes the file to object storage and records the database Artifact in one tool call. AgentCore and the Agent never receive MinIO credentials.
Configuration:
AGENTCORE_ARTIFACT_TOOL_TOKEN: shared credential injected by AgentCore.- Maximum file size: 10 MiB.
The expert includes returned Artifact IDs in its terminal event:
{
"type": "RUN_SUCCEEDED",
"payload": {
"resultText": "Completed analysis...",
"artifactIds": ["artifact-..."]
}
}artifactFileIds remains supported only for the in-process legacy Mock.
GET /api/v1/projects/{projectId}/tasks/{taskId}/workspace
Response:
{
"project": {},
"task": {},
"messages": [],
"events": [],
"plans": [],
"tasks": [],
"humanRequests": [],
"artifacts": []
}This is a read-oriented aggregate endpoint. It returns public events only. Several nested fields are raw database JSON strings and should be treated as diagnostic data rather than a stable domain schema.
Prompt templates are stored in MySQL and are not loaded from project resource files.
| Prompt key | Scene | Purpose |
|---|---|---|
coordinator.execution |
COORDINATOR_EXECUTION |
Intent routing, delegation context and output protocol |
coordinator.planning |
COORDINATOR_PLANNING |
Expert task decomposition and dependency planning |
expert.execution |
EXPERT_EXECUTION |
Subtask background, acceptance criteria and communication protocol |
expert.resume |
EXPERT_RESUME |
Resume an expert task after human input |
Templates use {{context_json}} for runtime context. Dynamic content is
serialized as JSON and treated as untrusted task data.
GET /api/v1/admin/prompts?promptKey=expert.execution
Requires a user listed in PROMPT_ADMIN_USERS.
POST /api/v1/admin/prompts
{
"promptKey": "expert.execution",
"agentScope": "EXPERT_COMMON",
"scene": "EXPERT_EXECUTION",
"templateContent": "Instructions...\n{{context_json}}",
"variablesSchema": "{\"required\":[\"context_json\"]}"
}The server assigns the next immutable version and creates it as DRAFT.
POST /api/v1/admin/prompts/{promptId}/publish
The selected version becomes PUBLISHED; the prior version becomes
RETIRED. Each render is recorded in prompt_execution with its template,
version, Agent, scene, variables snapshot and rendered Prompt.
GET /health
{
"status": "UP",
"service": "TeamCoordinator",
"time": "2026-08-03T10:00:00Z"
}GET /ready
{
"status": "READY",
"service": "TeamCoordinator",
"time": "2026-08-03T10:00:00Z"
}Spring Boot probes are also available below /actuator, including
/actuator/health.
The following is the outbound contract expected by Coordinator. Paths are configurable through environment variables.
| Operation | Default method and path | Configuration |
|---|---|---|
| Submit | POST /runs |
AGENTCORE_SUBMIT_PATH |
| Status | GET /runs/{sessionId} |
AGENTCORE_STATUS_PATH |
| Stream | GET /runs/{sessionId}/streamEvents |
AGENTCORE_STREAM_PATH |
| Stop or answer | POST /runs |
AGENTCORE_SUBMIT_PATH |
Base URL: AGENTCORE_BASE_URL.
Authentication:
Authorization: <AGENTCORE_AUTH_VALUE>The header name can be changed with AGENTCORE_AUTH_HEADER.
{
"type": "userInput",
"sessionId": "",
"systemPrompt": "Database-managed coordinator or expert system prompt",
"data": {
"skillNames": ["cmb-ui-design"],
"skillOrigin": "skillMarket",
"contents": [{"type": "text", "value": "Analyze API risks"}],
"context": [{"type": "text", "value": "{\"projectName\":\"Risk Review\"}"}],
"attachments": [{
"fileName": "requirements.pdf",
"fileDownloadUrl": "https://minio.example/presigned-url"
}]
}
}sessionId is empty for a new AgentCore conversation. Prompts are loaded from
the Coordinator database. Attachments use directly downloadable MinIO
pre-signed URLs; AgentCore does not receive MinIO credentials.
{
"returnCode": "SUC0000",
"data": {
"sessionId": "run-...",
"conversationId": "conversation-...",
"queuePosition": 0
}
}Errors use {"returnCode":"K8S6004","message":"入参不合法"}. Coordinator-side
idempotency remains in MySQL and is not sent in this AgentCore payload.
Coordinator and expert calls share the wire format. Their database prompt templates and hidden context construction differ. Coordinator context contains the overall request and conversation; expert context contains the delegated subtask, background, expected output, acceptance criteria and protocol.
GET /runs/{sessionId}
Response is the latest AgentRunEvent, or 404 when the run is unknown.
GET /runs/{sessionId}/streamEvents?afterSequence={sequence}
Request headers:
Content-Type: application/json
Accept: text/event-stream
Authorization: ...The request has no JSON body. Despite the JSON request content type, the response must use:
Content-Type: text/event-streamEach data: chunk is JSON and has a globally unique top-level eventId beside
type. Example:
data:{"type":"liveStatus","content":"模型响应中","timestamp":1769482300130,"eventId":"evt-1"}
data:{"type":"chat","content":"分析完成","timestamp":1769482300131,"eventId":"evt-2"}
data:{"type":"end","attachments":[],"timestamp":1769482300132,"eventId":"evt-3"}
Supported raw types include taskInQueue, liveStatus, planUpdate,
newPlanStep, confirm, chat, streamStart, textDelta, streamEnd,
thinking events, tool events, subagent events, file events, error and end.
All original fields are retained in the normalized event payload. Coordinator
deduplicates by eventId; its numeric sequence is a local database ordering
cursor, not an AgentCore identity.
{"type":"stopSession","sessionId":"run-..."}{
"type":"userAnswerQuestion",
"sessionId":"run-...",
"data":{
"questionId":"agentcore-question-id",
"answers":{"写作主题":"武侠","写作风格":"简洁直白"}
}
}The Coordinator agent must return a schema-valid decision:
{
"sessionId": "run-...",
"sequence": 3,
"type": "RUN_SUCCEEDED",
"status": "SUCCEEDED",
"payload": {
"decision": {
"decision_type": "CREATE_PLAN",
"task_intent": {
"intent": "ANALYZE",
"objective": "Analyze API risks",
"expected_outputs": ["Risk report"],
"constraints": [],
"required_capabilities": ["analysis"],
"input_refs": [],
"missing_information": [],
"risk_level": "MEDIUM",
"execution_mode": "SINGLE_EXPERT"
}
}
}
}payload.decision may also be a JSON string. A result may alternatively be
placed in payload.resultText, but it must contain the complete decision JSON.
{
"sessionId": "run-...",
"sequence": 3,
"type": "RUN_SUCCEEDED",
"status": "SUCCEEDED",
"payload": {
"expertId": "analysis-expert",
"resultText": "Completed analysis...",
"artifactIds": ["artifact-..."]
}
}payload.resultText is required and must be non-empty. artifactIds is
optional. Every ID must have been created by the upload_artifact tool for
the same Agent run. artifactFileIds is a legacy in-process Mock field.
{
"sessionId": "run-...",
"sequence": 3,
"type": "RUN_WAITING_HUMAN",
"status": "WAITING_HUMAN",
"message": "More information is required.",
"payload": {
"question": "Which environment should be analyzed?",
"requestType": "CLARIFICATION"
}
}payload.question is required by the Coordinator workflow.
POST /runs/{sessionId}/cancel
Response: terminal AgentRunEvent with type RUN_CANCELLED; 404 if the
session does not exist.
These endpoints are development-only and should not be exposed in production.
GET /mock/experts
[
{
"expertId": "analysis-expert",
"displayName": "Analysis Expert",
"enabled": true,
"available": true,
"concurrencyLimit": 2,
"capabilities": ["analysis"]
}
]POST /mock/agentcore/runs
Uses the AgentCore submit request and returns 202 Accepted.
GET /mock/agentcore/runs/{sessionId}
GET /mock/agentcore/runs/{sessionId}/streamEvents?afterSequence=0
Response: text/event-stream.
Stop and HITL answers are posted to /mock/agentcore/runs with the same
stopSession and userAnswerQuestion request bodies as real AgentCore.
POST /mock/files/presign
{
"fileName": "input.txt",
"contentType": "text/plain"
}Response:
{
"fileId": "mock-file-...",
"fileName": "input.txt",
"contentType": "text/plain",
"size": 0,
"checksum": null,
"uploadUrl": "/mock/files/mock-file-.../content",
"downloadUrl": "/mock/files/mock-file-.../content"
}PUT /mock/files/{fileId}/content
Request body: raw binary bytes.
GET /mock/files/{fileId}
GET /mock/files/{fileId}/content
Response content type: application/octet-stream.
DELETE /mock/files/{fileId}
Response: 204 No Content, or 404 if not found.
| Environment variable | Default | Purpose |
|---|---|---|
AGENTCORE_MOCK_ENABLED |
true |
Select local mock or remote HTTP adapter |
AGENTCORE_BASE_URL |
Empty | Remote AgentCore base URL |
COORDINATOR_AGENT_ID |
coordinator |
Coordinator agent identifier |
AGENTCORE_SESSION_HEADER |
X-Session-Id |
Business Task session header |
AGENTCORE_AUTH_HEADER |
Authorization |
Authentication header name |
AGENTCORE_AUTH_VALUE |
Empty | Authentication header value |
AGENTCORE_SUBMIT_PATH |
/runs |
Submit endpoint |
AGENTCORE_STATUS_PATH |
/runs/{sessionId} |
Status endpoint |
AGENTCORE_STREAM_PATH |
/runs/{sessionId}/streamEvents |
SSE endpoint |
AGENTCORE_CANCEL_PATH |
/runs/{sessionId}/cancel |
Cancel endpoint |
MYSQL_URL |
Local xservice database |
Coordinator state database |
MYSQL_USERNAME |
root |
Database user |
MYSQL_PASSWORD |
Empty | Database password |
MINIO_ENDPOINT |
http://127.0.0.1:9000 |
Object storage endpoint |
MINIO_ACCESS_KEY |
minioadmin |
Object storage access key |
MINIO_SECRET_KEY |
minioadmin |
Object storage secret |
MINIO_BUCKET |
digital-team |
Object storage bucket |
DIGITAL_TEAM_MVP_ENABLED |
true |
MVP feature flag |
DIGITAL_TEAM_EMERGENCY_STOP |
false |
Emergency kill switch |