Skip to content

Web / API Access (WebChat) Guide

MateClaw's WebChat channel lets external websites reach the conversation engine over plain HTTP / SSE, with no JWT. Visitor identity is isolated under a shared API Key via visitorId + visitorToken (HMAC-signed).

There are two integration paths:

  • Embeddable widget — drop in one JS file, call init(...) once, and a chat bubble appears in the corner. Fastest to ship; ideal for marketing sites / landing-page support.
  • Custom HTTP / SSE integration — call the REST + SSE endpoints below and render your own UI. For deeply customized experiences.

Embeddable widget (mateclaw-webchat)

The widget is a zero-dependency browser library shipped in both UMD (<script> tag) and ESM (npm) formats.

Option 1: script tag (UMD)

html
<script src="https://<your-deployment>/mateclaw-webchat.umd.js"></script>
<script>
  MateClawWebChat.init({
    apiKey: 'your-channel-api-key',   // from the channel edit page
    server: 'https://<your-deployment>',
    title: 'Support',
    placeholder: 'Type a message...'
  })
</script>

Option 2: npm (ESM)

bash
npm install @mateclaw/webchat
ts
import { init } from '@mateclaw/webchat'

init({ apiKey: 'your-channel-api-key', server: 'https://<your-deployment>' })

Config options

FieldRequiredDefaultNotes
apiKeyyesChannel API Key
serveryesMateClaw server URL (no trailing slash)
positionnobottom-rightBubble position: bottom-right / bottom-left
primaryColorno#D97757Primary color (any CSS color)
titlenoMateClawPanel title
placeholdernoType a message...Input placeholder

Behavior

  • The visitor ID is generated on first open and persisted in localStorage (key mc-webchat-visitor), then reused — you don't manage it yourself.
  • The panel is themed entirely through CSS variables (--mc-primary / --mc-bg-elevated / ...); the host page can override them under :root.
  • The widget consumes the /stream SSE protocol described below. For richer interactions (session list, attachments, revocation), call the HTTP endpoints directly and build your own UI.

Custom integration: basics

  • Base URL: https://<your-MateClaw-deployment>/api/v1/channels/webchat
  • Auth: every endpoint requires the header X-MC-Key: <API Key> (from the channel edit page).
  • Session-management endpoints additionally require X-MC-Visitor-Token: <HMAC> (issued by the server and returned on the first /stream call).
  • Response envelope: R<T>{"code": 200, "msg": "...", "data": T}; anything other than 200 is an error.
  • Charset: UTF-8. The SSE stream uses text/event-stream; charset=UTF-8.

Endpoint list

MethodPathAuthPurpose
POST/streamAPI KeySSE streaming chat (issues visitorToken)
GET/configAPI KeyGet channel config (title/placeholder/...)
POST/sessionsAPI KeyExplicitly create an empty session thread
GET/sessions+ visitorTokenList sessions (excludes archived by default)
GET/sessions/page+ visitorTokenPaginated + keyword search
PUT/sessions/title+ visitorTokenRename
PUT/sessions/pinned+ visitorTokenPin / unpin
PUT/sessions/archive+ visitorTokenArchive / unarchive
DELETE/sessions+ visitorTokenDelete
POST/sessions/stop+ visitorTokenStop an in-flight stream
POST/sessions/regenerate+ visitorTokenRegenerate the last assistant reply
POST/sessions/approve+ visitorTokenApprove a pending tool approval and replay (SSE)
POST/sessions/deny+ visitorTokenDeny a pending tool approval (synchronous JSON)
GET/sessions/messages+ visitorTokenMessage list (paginated)
POST/upload+ visitorTokenUpload an attachment (returns fileId)
GET/files+ visitorTokenDownload a file (uploaded or agent-generated)

Admin-level (require a MateClaw JWT, outside the permitAll set above):

MethodPathPurpose
POST/api/v1/admin/webchat/revoked-visitorRevoke a visitor's management token
DELETE/api/v1/admin/webchat/revoked-visitorUn-revoke

In the admin console, the "Conversations" list hides WebChat visitor sessions from regular admins by default — only a global admin sees them. This is a cross-workspace isolation and visitor-privacy guard.

Auth flow

text
┌──────────┐  POST /stream {visitorId:"v1", message:"hi"}
│  Client  │ ─────────────────────────────────────────────► ┌──────────┐
└──────────┘                                                  │ MateClaw │
   ▲                                                          └──────────┘
   │  SSE meta event: {sessionId, conversationId, visitorToken}
   │  SSE content_delta events: {text}
   │  SSE done event
   └─────────────────────────────────────────────────────────

┌──────────┐  GET /sessions X-MC-Visitor-Token: <visitorToken>│
│  Client  │ ─────────────────────────────────────────────► │
└──────────┘ ◄──── 200 {code:200, data:[...]}                │

visitorToken is valid for 7 days by default; re-issue it through any /stream call once it expires. Every /stream call (even with a still-valid token) returns a fresh token in the meta event — the client should keep updating its stored copy.

Error codes

HTTPWhen
400Invalid parameter (visitorId / sessionId charset, title length, etc.)
401Invalid API Key / missing, expired, or revoked visitorToken
404The given sessionId does not exist or does not belong to the visitor
409More than 5 inactive empty sessions

The error message is in R.msg and can be shown directly to the user.

SSE event protocol

/stream and /sessions/regenerate return text/event-stream:

event: meta
data: {"sessionId":"s1","conversationId":"webchat:abc123:v1:s1","visitorToken":"xxx.yyy"}

event: phase
data: {"phase":"planning","timestamp":1716700000000}

event: tool_start
data: {"tool":"web_search"}

event: tool_end
data: {"tool":"web_search","success":true}

event: plan
data: {"steps":["search the web","summarize"]}

event: content_delta
data: {"text":"He"}

event: content_delta
data: {"text":"llo"}

event: thinking_delta
data: {"text":"..."}    (optional, reasoning trace)

event: done
data: {"status":"completed"}

event: error
data: {"message":"..."}  (on failure)

The SSE spec requires clients to ignore unknown event types. The server may emit internal events prefixed with an underscore (e.g. _usage_final); these carry no contract for visitors and can be safely ignored.

Optional real-time progress events

phase / tool_start / tool_end / plan are optional events — used to show an "AI is typing…" bubble, tool-execution badges ("Searching…"), or a Plan-and-Execute step checklist in your SDK. The SDK can ignore them all and still render the full reply from content_delta alone.

EventTriggered whenData fields
phasethe agent enters a new execution phase (planning / generating / summarizing / ...)phase, timestamp
tool_startthe agent calls a tooltool (tool name)
tool_enda tool call finishestool, success
plana Plan-and-Execute agent breaks work into stepssteps (string array)

Note: tool_start / tool_end carry only the tool name, never the call arguments or results — agent tool calls may involve PII (file paths, user queries, credentials), which would leak if forwarded to a third-party website frontend. The SDK should map tool names to localized labels (web_search → "Searching…").

File upload / download

  1. POST /upload (multipart): returns {fileId, fileName, contentType, size}.
  2. Add the fileId to the attachmentIds array in the body of the next /stream call. Unknown / expired / foreign fileIds are silently dropped (only the text part is sent, no error).
  3. The agent reads server-side files directly; the fileUrl in a message is a relative download path (/api/v1/channels/webchat/files?storedName=...) — the client appends auth headers to download.
  4. Agent-generated files (PDF/DOCX/...) appear in assistant replies as /api/v1/files/generated/<uuid> URLs, downloadable without auth, with a 7-day TTL.

Session lifecycle: pin / archive / delete

  • Pin (PUT /sessions/pinned): sorted first in the /sessions list.
  • Archive (PUT /sessions/archive): a soft close — the thread stays in the DB (history queryable, addressable by sessionId, files downloadable) but is hidden from /sessions by default (pass includeArchived=true to return it), and no longer counts against the "≤ 5 inactive empty sessions" quota.
  • Delete (DELETE /sessions): permanent, unrecoverable.

Each session returned by /sessions includes: sessionId, title, lastActiveTime, messageCount, pinned, archived, streamStatus (running / idle).

Tool approval resolve (API-Key channel)

When an agent bound to WebChat calls a tool protected by Tool Guard, that turn suspends waiting for approval. The visitor can approve or deny it in-session instead of letting it time out.

  • Approve POST /sessions/approve — with sessionId + pendingId. Auth reuses visitorToken + conversation ownership; the pendingId is strictly validated to belong to this session (else 404), closing a cross-visitor IDOR. Approving replays the suspended tool call and resumes as SSE.
  • Deny POST /sessions/deny — with sessionId + pendingId, returns synchronous JSON, no replay.

Both broadcast a tool_approval_resolved SSE event (see Optional realtime progress events above) so the SDK / frontend clears the approval banner in real time.

Whether an approval appears depends on whether the agent's bound Tool Guard rules set require_approval for some tool. Get pendingId from the tool_approval_requested event.

bash
# Approve (resumes as SSE)
curl -N -X POST "https://mate.example.com/api/v1/channels/webchat/sessions/approve" \
  -H "X-MC-Key: <API Key>" -H "X-Visitor-Token: <visitorToken>" \
  -H "Content-Type: application/json" \
  -d '{"sessionId":"s1","pendingId":"<pendingId>"}'

# Deny (synchronous JSON)
curl -X POST "https://mate.example.com/api/v1/channels/webchat/sessions/deny" \
  -H "X-MC-Key: <API Key>" -H "X-Visitor-Token: <visitorToken>" \
  -H "Content-Type: application/json" \
  -d '{"sessionId":"s1","pendingId":"<pendingId>"}'

visitorToken revocation (admin)

A visitor abusing the channel? An admin calls:

bash
curl -X POST https://mate.example.com/api/v1/admin/webchat/revoked-visitor \
  -H "Authorization: Bearer <admin JWT>" \
  -H "Content-Type: application/json" \
  -d '{"channelId":123, "visitorId":"v1", "reason":"abuse"}'

After revocation, all of that visitor's management endpoints return 401 (/stream is unaffected and can re-issue a fresh token). Revocation state is briefly cached, so under a multi-instance deployment it takes up to ~10 minutes to fully propagate. Un-revoke via DELETE on the same endpoint.

curl examples

Step 1: send the first message

bash
curl -N -X POST https://mate.example.com/api/v1/channels/webchat/stream \
  -H "X-MC-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{"visitorId":"v1","message":"hi"}'

Save the visitorToken and sessionId from the meta event.

Step 2: list sessions

bash
curl https://mate.example.com/api/v1/channels/webchat/sessions?visitorId=v1 \
  -H "X-MC-Key: your-api-key" \
  -H "X-MC-Visitor-Token: <from step 1>"

Step 3: upload an attachment and send

bash
# upload
curl -X POST https://mate.example.com/api/v1/channels/webchat/upload \
  -H "X-MC-Key: your-api-key" \
  -H "X-MC-Visitor-Token: <token>" \
  -F "visitorId=v1" \
  -F "file=@report.pdf"
# returns {"fileId":"abc-uuid", ...}

# send a message with the attachment
curl -N -X POST https://mate.example.com/api/v1/channels/webchat/stream \
  -H "X-MC-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{"visitorId":"v1","sessionId":"<sid>","message":"take a look at this report","attachmentIds":["abc-uuid"]}'

Limits

  • Inactive empty sessions per visitor ≤ 5 (creation is rejected past 5 — send a message or delete an old session first)
  • Upload: single file ≤ configured cap, extension + MIME dual whitelist; ≤ 50 files / 200 MB per session (configurable)
  • visitorToken expires in 7 days; agent-generated file URLs have a 7-day TTL
  • Currently a single-instance deployment (staging registry + streamTracker are both in-memory). Multi-instance support is on the roadmap.