Core API Flow
此内容尚不支持你的语言。
Wetel is a headless agent backend: you speak GraphQL mutations and subscriptions to it, and it speaks events back to you. There is no pre-built chat widget shipped as part of the product — you own 100% of the UI. This page documents the three mutations that drive the runtime session lifecycle: sdkStart, sdkSendMessage, and sdkEndSession.
If you haven’t set up authentication yet, read Authentication first — this page assumes you already have a long-lived API key and know how to obtain a session’s short-lived avatarToken. If you haven’t configured an agent yet, see Agents.
Every request needs two headers
Section titled “Every request needs two headers”Every mutation and query against the Wetel API — no exceptions — requires the x-huat-platform: customer header, in addition to whatever auth header the specific call needs (X-Api-Key or Authorization: Bearer <token>). Omitting it returns a 403 Forbidden before your auth is even checked. The one place it’s carried differently is the WebSocket connection_init payload, described below, where it goes inside connectionParams instead of a plain HTTP header.
All examples below target the public endpoint:
https://api.wetel.dev/graphqlThe session lifecycle, end to end
Section titled “The session lifecycle, end to end”- Your backend starts a session for an end user by calling
sdkStart, authenticated with your long-lived API key. This returns asessionIdand a short-livedavatarToken. - Your backend hands the
avatarTokenandsessionIdto your frontend (cookie,sessionStorage, URL param — whatever transport you use). - Your frontend opens a GraphQL subscription (
sessionEvents) authenticated with theavatarToken, to receive the agent’s replies and workflow events. See Events & Subscriptions for the full event catalogue and a critical gotcha about union fragments. - Your frontend sends user messages via
sdkSendMessage, authenticated with theavatarToken. This call returns an immediate acknowledgement — not the agent’s reply. Message text is capped at 4,000 characters — validated on the field, rejected outright rather than truncated. If you’re sending something larger than a normal chat message (a long document, a large structured payload for classification, an entire transcript), you’ll need to summarize or chunk it client-side before callingsdkSendMessage. - The agent’s reply streams back asynchronously over the subscription opened in step 3, as one or more
AiResponseEventpayloads. - When the conversation ends, your frontend (or backend) calls
sdkEndSession, which closes out the session and emits a terminalSessionEndedEvent.
Your Backend Your Frontend Wetel | | | |--- sdkStart(agentId) ----------------------------------->| |<---------------- { sessionId, avatarToken } -------------| |--- hand off sessionId + avatarToken --->| | | |--- connection_init ------->| | |<----- connection_ack -------| | |--- subscribe(sessionId) -->| | |--- sdkSendMessage(text) -->| | |<----- true (ack only) ------| | |<== AiResponseEvent (chunk) =| | |<== AiResponseEvent (final) =| | |--- sdkEndSession -------->| | |<-------- { durationSeconds } | | |<==== SessionEndedEvent =====|Step 1: Start a session (backend)
Section titled “Step 1: Start a session (backend)”Call this from your backend, using your long-lived X-Api-Key. Never call sdkStart from a browser — the API key must never reach client-side code.
mutation StartSession { sdkStart( input: { agentId: 3, clientId: "end-user-abc123", position: "helpdesk" } ) { sessionId avatarToken graphqlEndpoint }}Headers:
X-Api-Key: sk_your_api_key_herex-huat-platform: customerInput fields:
agentId(optional) — which agent to use. If omitted, a default agent is selected.clientId(optional) — your own end-user identifier, useful for analytics and audit trails.position,useCase(optional) — metadata for logging.
Response fields:
sessionId— an integer. You need this for every subsequent message and the event subscription.avatarToken— a short-lived (~24 hour) bearer token. Hand this to your frontend; it’s the only credential your client-side code should ever hold.graphqlEndpoint— the GraphQL HTTP endpoint to use for this session. Prefer reading this from the response over hardcoding a URL, so your integration doesn’t silently break if the endpoint ever changes.
Step 2: Send a message (frontend) — read this carefully
Section titled “Step 2: Send a message (frontend) — read this carefully”This is the single most common integration mistake, so it gets its own heading.
mutation SendMessage { sdkSendMessage( input: { sessionId: 42 text: "What books do you have on quantum computing?" } )}Headers:
Authorization: Bearer <avatarToken>x-huat-platform: customerResponse: true — immediately, as soon as the message is accepted. This is an acknowledgement, not the reply.
The agent’s actual response is generated asynchronously — an LLM call can take anywhere from a couple of seconds to ten or more — and streams back over the sessionEvents subscription you opened in Step 3, as one or more AiResponseEvent payloads. If you write code that waits on the sdkSendMessage mutation’s response and expects the agent’s text to be inside it, your integration will appear to “not respond” — the reply already arrived over the subscription channel, not the mutation.
The correct pattern:
- Open (or already have open) the
sessionEventssubscription for thissessionId. - Call
sdkSendMessageand discard/ignore its boolean response beyond confirming it’strue. - Render the reply as
AiResponseEventchunks arrive on the subscription, concatenating until you seeisFinal: true.
See Events & Subscriptions for the full subscription setup, the complete event list, and a separate gotcha about a race condition between subscribing and sending your first message.
Step 3: Subscribe to events
Section titled “Step 3: Subscribe to events”Covered in full detail, including the exact subscription query shape and every event type, in Events & Subscriptions. At minimum, your subscription must be open before (or immediately after) you send your first message, since that’s how the reply reaches your client at all.
Step 4: End the session
Section titled “Step 4: End the session”mutation EndSession { sdkEndSession(input: { sessionId: 42 }) { sessionId durationSeconds evaluation { rating feedback } }}Headers:
Authorization: Bearer <avatarToken>x-huat-platform: customerCall this when the user closes the chat, or after your own idle timeout. The response includes session metadata (duration, and an evaluation summary if one was generated — evaluation generation is asynchronous and only runs for conversations with two or more turns, so evaluation may be null even on a valid call). The sessionEvents subscription closes shortly after, and a terminal SessionEndedEvent is emitted first so your client can react before the connection drops.
CORS: a valid API key does not get you connectivity
Section titled “CORS: a valid API key does not get you connectivity”Authentication and CORS are two separate gates. Even with a fully valid X-Api-Key or avatarToken, a browser will refuse to let your frontend talk to the Wetel API at all if your origin isn’t allow-listed server-side — this applies to both the HTTP GraphQL endpoint and the WebSocket subscription upgrade.
Common hosting platforms (Vercel preview URLs, Cloud Run URLs) are typically covered automatically. If you’re deploying from a different origin — your own custom domain, a different hosting provider — that origin needs to be explicitly allow-listed on the Wetel side before your client can connect. If you hit an inexplicable CORS failure on an otherwise-correct integration, this is almost always the cause: reach out to coordinate getting your production/staging origin added to the allowlist before you go live.
An alternative: your backend holds the socket instead
Section titled “An alternative: your backend holds the socket instead”Everything above describes Wetel’s default recommended architecture — the browser holds the avatarToken and talks to Wetel directly for steps 3 and 4. This is the right choice for most integrations. If you specifically want backend-native message persistence, moderation before delivery, or a single reconnection strategy you control, see Backend-Proxied Relay Architecture for the alternative: your backend holds the sessionEvents subscription itself and relays replies to your frontend over a channel of your choosing — the browser never talks to Wetel at all.
Next steps
Section titled “Next steps”- Getting Started — obtaining your first API key and agent.
- Authentication — the two-tier API key / avatar token model in full.
- Agents — configuring the agent you’re starting sessions against.
- Backend-Proxied Relay Architecture — an alternative architecture where your backend, not the browser, holds the WebSocket.
- Troubleshooting — common integration failures and their fixes.