API and Event Contracts
API and Event Contracts
หัวข้อที่มีชื่อว่า “API and Event Contracts”1. Browser to BFF (/api/ai/chat and /api/ai/stream)
หัวข้อที่มีชื่อว่า “1. Browser to BFF (/api/ai/chat and /api/ai/stream)”Key payload fields:
{ "userMessage": "User input", "threadId": "thread_uuid", "agentType": "idea", "projectId": "project_uuid", "metadata": { "knowledge_search": true, "attached_file_ids": ["doc_or_file_uuid"] }}Current validation behavior in BFF proxy:
- Request keys are allowlisted.
- Metadata keys are allowlisted.
- Unknown request or metadata keys return
400. - If both
threadIdandsessionIdare provided and mismatch, BFF returns400.
Thread field normalization:
threadIdis the canonical UI/BFF field.sessionIdis still accepted as a deprecated alias for backward compatibility.- Preferred runtime behavior is a real UUID from the UI thread creation flow.
- If missing or
new, BFF returns400and requires thread creation first.
Naming Standard (Current)
หัวข้อที่มีชื่อว่า “Naming Standard (Current)”| Boundary | Canonical Name | Accepted Alias | Note |
|---|---|---|---|
| Browser -> BFF chat thread key | threadId | sessionId | BFF rejects mismatched values when both exist. |
| Browser -> BFF resume decision key | toolCallId | tool_call_id, id | BFF normalizes outbound decisions to snake_case for engine. |
| BFF -> Engine chat thread key | sessionId | - | Engine schema maps sessionId into thread_id. |
| BFF -> Engine resume decision key | tool_call_id | - | Engine resume contract is snake_case. |
2. BFF to Engine (/api/v1/chat*)
หัวข้อที่มีชื่อว่า “2. BFF to Engine (/api/v1/chat*)”BFF sends internal contract headers on every engine call:
Content-Type: application/jsonAuthorization: Bearer <ENGINE_SERVICE_AUTH_TOKEN>(when configured)X-Contract-Version: 2026-02X-Request-Id: <request_id>X-User-Id: <user_uuid>X-Project-Id: <project_uuid>(when present)X-Thread-Id: <thread_uuid>(when present)X-Agent-Type: <agent_type>Accept: text/event-streamfor streaming endpoints
Engine validates these headers against body claims when INTERNAL_API_ENFORCE=true.
Forwarded chat body shape
หัวข้อที่มีชื่อว่า “Forwarded chat body shape”{ "userMessage": "User input", "sessionId": "thread_uuid", "agentType": "idea", "projectId": "project_uuid", "userId": "user_uuid", "provider": "groq", "model": null, "systemPrompt": "...", "chatHistory": null, "metadata": { "knowledge_search": true, "attached_document_ids": ["resolved_doc_uuid"], "canvas_context": { "mode": "summary", "summary": { "nodeCount": 10, "byType": { "strategy_item": 5 }, "byStatus": { "approve": 2, "reject": 1, "pending": 7 } }, "context_ref": { "project_id": "project_uuid", "thread_id": "thread_uuid", "agent_id": "strategy", "revision": "0123456789abcdef" } }, "canvas_profile": {} }}3. Resume Contract (/api/ai/resume -> /api/v1/chat/resume)
หัวข้อที่มีชื่อว่า “3. Resume Contract (/api/ai/resume -> /api/v1/chat/resume)”Browser to BFF:
{ "threadId": "thread_uuid", "projectId": "project_uuid", "agentType": "idea", "approved": true, "reason": "optional", "decisions": [ { "toolCallId": "tool_1", "approved": true }, { "toolCallId": "tool_2", "approved": false, "reason": "not needed" } ]}BFF to engine (normalized):
{ "thread_id": "thread_uuid", "project_id": "project_uuid", "user_id": "user_uuid", "action_id": null, "approved": true, "reason": "optional", "decisions": [ { "tool_call_id": "tool_1", "approved": true, "reason": null }, { "tool_call_id": "tool_2", "approved": false, "reason": "not needed" } ]}If decisions[] is present but malformed, BFF returns 400.
4. Canvas API Contracts (/api/canvas/*)
หัวข้อที่มีชื่อว่า “4. Canvas API Contracts (/api/canvas/*)”Workspace
หัวข้อที่มีชื่อว่า “Workspace”GET /api/canvas/workspace
Required query:
agentIdprojectId
Optional query:
threadIdincludeEdgesincludeAllAgentslimit
Context
หัวข้อที่มีชื่อว่า “Context”GET /api/canvas/context
Required query:
agentIdprojectId
Optional query:
threadIdmaxNodesincludeContentincludeRelationshipsframework
Mutations
หัวข้อที่มีชื่อว่า “Mutations”POST /api/canvas/mutations
{ "agentId": "strategy", "projectId": "project_uuid", "threadId": "thread_uuid", "idempotencyKey": "client-generated-key", "operations": [ { "operation": "update_node", "nodeId": "node_uuid", "data": { "title": "Updated" } } ]}All /api/canvas/* routes require a real authenticated user token.
cURL examples (Postman import-ready, canvas read APIs)
หัวข้อที่มีชื่อว่า “cURL examples (Postman import-ready, canvas read APIs)”Postman Import -> Raw text works best with single-line cURL (no trailing \ and no shell env variables).
GET /api/canvas/workspace:
curl --location --request GET "http://localhost:3000/api/canvas/workspace?agentId=strategy&projectId=<project_uuid>&threadId=<thread_uuid>&includeEdges=true&includeAllAgents=false&limit=200" --header "Authorization: Bearer <USER_ACCESS_TOKEN>"GET /api/canvas/context:
curl --location --request GET "http://localhost:3000/api/canvas/context?agentId=strategy&projectId=<project_uuid>&threadId=<thread_uuid>&maxNodes=50&includeContent=true&includeRelationships=true&framework=swot" --header "Authorization: Bearer <USER_ACCESS_TOKEN>"If you prefer Postman environment variables, replace values with {{API_BASE}}, {{USER_TOKEN}}, {{PROJECT_ID}}, {{THREAD_ID}}, and {{AGENT_ID}} after import.
5. Internal Canvas Context Contract
หัวข้อที่มีชื่อว่า “5. Internal Canvas Context Contract”GET /api/internal/canvas/context is internal-only for engine service calls.
Required:
- Bearer token matching
INTERNAL_ENGINE_TOKEN X-Contract-VersionmatchingINTERNAL_API_CONTRACT_VERSION- trusted scope headers (
X-User-Id,X-Project-Id,X-Thread-Id)
Query projectId/threadId/agentId must match trusted claims or route returns 403.
Example internal call (Postman import-ready):
curl --location --request GET "http://localhost:3000/api/internal/canvas/context?agentId=strategy&projectId=<project_uuid>&threadId=<thread_uuid>&includeContent=true&includeRelationships=true" --header "Authorization: Bearer <INTERNAL_ENGINE_TOKEN>" --header "X-Contract-Version: 2026-02" --header "X-User-Id: <user_uuid>" --header "X-Project-Id: <project_uuid>" --header "X-Thread-Id: <thread_uuid>"6. Streaming Event Notes
หัวข้อที่มีชื่อว่า “6. Streaming Event Notes”Engine emits SSE payloads with keys such as:
chunktool_callsearch_resultstype: hitl_interruptdoneerror
BFF stream relay removes canvas_mutation_intents fields before forwarding to browser.
7. Fast 400/403 Triage
หัวข้อที่มีชื่อว่า “7. Fast 400/403 Triage”400 Unknown request field(s)orUnknown metadata field(s): browser payload contains disallowed keys.400 Ambiguous thread identifier:threadIdandsessionIdmismatch.400 Thread ID is required: thread not materialized yet (threadIdmissing or set tonew).400 Invalid decisions payload: malformeddecisions[](missingtoolCallId/tool_call_idorapproved).400 Invalid internal contract version: BFF and engineINTERNAL_API_CONTRACT_VERSIONmismatch.403 Thread not found or access denied: thread/user/project scope mismatch.