WebChat
General description
Webchat is a small application allowing communication between users of the website and our bots
Similar to Intercom, webchat will be added to the client page, and be displayed in the right bottom corner, ready to use.
There Is a separate section about the usage of WebChat, starting from What is webchat?
Mobile SDK backend
Besides serving the browser widget, this service is also the gRPC backend for the SentiOne Automate Mobile SDK (iOS and Android). Native mobile applications do not go through the Gateway API or a webhook — they connect to this service directly over gRPC, and it reuses the same session, conversation, hooks and handover machinery as the browser widget.
That makes web-chat the runtime for two channel families:
| Channel type | Client | Transport |
|---|---|---|
| WebChat | Browser widget | HTTP + WebSocket |
| Mobile - iOS / Mobile - Android | Native app embedding the Mobile SDK | gRPC (HTTP/2) |
Channel setup for the mobile variants is described in Adding the Mobile SDK Channel (iOS & Android).
Mobile channels are registered by the channels-connector through the internal /api/mobileChat/* endpoints and stored in this service's own mobile_chat_subscription table, which holds the App Key, the registered application identity, the authentication method, and the optional voice and handover configuration.
API
Default port: 5760
The application exposes two APIs on this port, with different security models:
- the public HTTP API, used by the browser widget, which is not secured;
- the gRPC API, used by the Mobile SDK, which is authenticated on every call.
No gRPC call is served anonymously. Handshake is admitted only when the App Key resolves to an active mobile channel and the application identity the SDK reports matches the one registered on that channel; every later RPC must carry the session token issued by that handshake. On channels that enforce external JWT, each call additionally carries a client access token that is validated against the configured JWKS, audience and user identifier claim — and on the long-lived Subscribe stream that token is re-checked as it expires, so the stream cannot outlive the credential behind it. Transport is TLS, terminated at the ingress; the SDK requires it and pins certificates for the SentiOne-hosted environments.
There are default endpoints for monitoring purposes. They are described in Components monitoring section.
gRPC API (Mobile SDK)
The gRPC service is exposed on the same port as the HTTP API — there is no separate gRPC listener. Requests whose Content-Type starts with application/grpc are dispatched to the gRPC handler; everything else is routed normally by Play.
Two consequences worth noting when deploying or debugging:
- HTTP/2 must be enabled (
pekko.http.server.preview.enable-http2 = on). Any proxy or ingress in front of the service has to pass HTTP/2 through, and must not buffer responses —Subscribe,SpeechToTextandTextToSpeechare long-lived streams. - Server reflection is enabled, so tools such as
grpcurlcan call the service without a local copy of the.protofiles.
Service: sentione.automate.mobile.v1.MobileChatService. The SDK calls Handshake first, then keeps a Subscribe stream open for the lifetime of the session; the remaining RPCs cover conversations, messaging and voice.
Every RPC carries its credentials in gRPC metadata rather than in the message body:
| Metadata header | Purpose |
|---|---|
x-sentione-app-key | App Key of the mobile channel; identifies the channel |
x-sentione-platform | android or ios; must agree with the platform identity sent in the handshake |
x-sentione-sdk-version | SDK version, checked against the kill-switch below |
x-sentione-session-token | Session token issued by Handshake; sent on every later RPC |
x-sentione-client-access-token | Per-call user credential, for channels that enforce external JWT; absent for anonymous channels |
The client's IP address is taken from the TCP connection and attached to the request by the gRPC handler, because gRPC calls carry no X-Forwarded-For.
Internal HTTP endpoints
| Endpoint | Called by | Auth |
|---|---|---|
POST /api/mobileChat/subscribe | channels-connector, when a mobile channel is created or updated | OTP |
POST /api/mobileChat/unsubscribe/:channelId | channels-connector, when a mobile channel is removed | OTP |
GET /api/handovers | admin, to list the handover types enabled on this installation | OTP |
Communication
This service communicates with the following services:
| Application | Config to URL | Auth |
|---|---|---|
| bot-integration | chatbots.senti-one-api.url | OTP |
| gateway-api | chatbots.gateway-api.url | none |
| hooks-server | chatbots.web-chat.hooks.hooks-receivers.url | OTP |
| thread-coordinator | chatbots.thread-coordinator-api.url | none |
| voice-gateway (TTS) | chatbots.voice-gateway-api.text-to-speech.base-url | API key |
| voice-gateway (ASR) | chatbots.voice-gateway-api.speech-to-text.endpoint | none |
| admin (projects) | chatbots.projects-api.url | OTP |
| storage | chatbots.storage-api.url | OTP |
Not every target is contacted by every installation:
- voice-gateway is used only by the Mobile SDK voice RPCs, and both entries are optional — each client is created only when its configuration block is present, so an installation that does not need speech in mobile apps can leave them out. TTS is called over HTTP (
POST /v2/synthesize); ASR is a gRPC dictation stream. Both are single, provider-agnostic endpoints: the speech vendor chosen on a channel does not change them. - admin (projects) and storage are used only when the attachments feature is configured (
chatbots.web-chat.attachments), to resolve the company owning a project and to store the uploaded files.
Config
Application's config:
chatbots.web-chat {
legacy-demo-enabled: true # enables or disables demo page for web chat.
# SQL connection details
db {
url: "jdbc:postgresql://db:5432/chatbots-web-chat",
user: "postgres",
password: "root"
max-connections: 20
}
session {
# After this duration of user inactivity, the session will be marked as expired
max-inactivity: 1h
# After this duration of user inactivity, the session will be permanently removed from the database
delete-after: 24h
}
# Mobile SDK (gRPC) settings. Only the two nested keys below are required;
# the rest have the defaults shown.
mobile-chat {
# Lifetime of the session token issued by the handshake. The SDK may reuse it
# to resume a conversation after a dropped connection.
session-token-ttl: 24h
# SDK version gate ("kill-switch")
sdk-version {
# Handshake is refused below this version, and the app cannot start a conversation
min-supported: "1.0.0"
# At or above min-supported but below this: handshake succeeds and is reported
# to the SDK as DEPRECATED, together with the hint below
deprecated-below: "1.0.0"
deprecation-hint: "A newer SDK version is available; please update."
}
# Tolerance applied when checking the expiry of an external JWT
client-access-token-clock-skew: 5s
# How often an open Subscribe stream re-checks its external JWT for expiry
client-access-token-expiration-check-interval: 5s
# Idle keep-alive on the Subscribe stream, so it never goes silent
subscribe-heartbeat-interval: 30s
}
hooks {
hooks-request-timeout: 30s
hooks-messages-max-age: 24h
hooks-receivers: [
{
url: "https://REACT_HOOKS_SERVER_PUBLIC_URL/webchat"
otp-secret: "PUT_SECRET_HERE"
}
]
}
# Public address this service is reachable at. Used to build the links handed
# out for attachments, so it must be the externally visible URL, not an
# internal service address.
public-url: "https://WEB_CHAT_PUBLIC_URL"
# OTP secret for the inbound thread webhook called by thread-coordinator
thread-coordinator-otp-secret: "PUT_SECRET_HERE"
# How long a resolved channel subscription is cached before it is re-read
subscription-cache-duration: 5s
# Allows sessions that are not tied to a configured channel
non-channel-web-chat-support-enabled: true
# Coordinates session processing across instances through Redis. Keep enabled
# whenever more than one instance is running.
distributed-locks-enabled: true
# Which handover destinations this installation offers. Reported to admin
# through GET /api/handovers and offered when configuring a channel.
handover-types {
react {
enabled: true
}
genesys {
enabled: true
}
}
# Attachments — optional. When absent, users cannot send files, and this
# service does not contact admin or storage at all.
attachments {
# Signs the generated attachment links
signature-secret-key: "PUT_SECRET_HERE"
# Virus scanning of uploaded files
cloudmersive {
api-key: "PUT_KEY_HERE"
url: "https://api.cloudmersive.com"
}
# Validity of a generated attachment link
link-expires-in: 1.day
}
}Other required configurations:
# Other APIs URLs
chatbots.gateway-api.url: "http://host:port"
# Bot Integration configuration
chatbots.senti-one-api {
url: "http://bot-integration:9010", # actual address for bot-integration service
secret: "KNSWG4TFORIGC43T" # same secret as defined in bot-integration service
}
# Redis configuration
chatbots.concurrency {
redis {
host: "redis"
port: 6379
}
}
# Required to properly work with web-chat channel. If it's not set the web-chat won't be displayed as an available channel
chatbots.admin.web-chat-demo-url: "http://host:port"Voice Gateway configuration (Mobile SDK voice only)
Needed only when mobile applications should use speech. Both blocks are optional
and independent — configure just the one you need.
These are not arbitrary URLs. Each one addresses a component of the Automate
platform itself:
text-to-speech→ Crocotta, the platform's text-to-speech componentspeech-to-text→ Pytia, the platform's speech recognition component
In practice both run in the same Kubernetes cluster as this service, so the host
is simply the Kubernetes service name and the traffic never leaves the cluster —
no ingress, public DNS or externally resolvable address is involved:
chatbots.voice-gateway-api {
# Text-to-speech synthesis — Crocotta, called over HTTP
text-to-speech {
base-url: "http://crocotta:PORT"
api-key: "PUT_KEY_HERE"
}
# Speech-to-text (dictation) — Pytia, called over gRPC.
# The port is Pytia's own `grpc-endpoint-port` (6885 as shipped).
speech-to-text {
endpoint {
hostname: "pytia"
port: 6885
}
# Upper bound on a single dictation stream
max-dictation-duration: 2m
}
}Substitute the service names your deployment actually uses. Outside a cluster —
for local development against a shared environment, for instance — these become
ordinary reachable addresses, typically port-forwards, and then the host must be
an address the container can reach rather than localhost.
These endpoints are provider-agnostic — they do not change per channelEach block points at one endpoint — Crocotta for TTS, Pytia for ASR. Neither is tied to a speech vendor, so this configuration is set once per installation and does not change when someone picks a different provider on a channel.
That is because the provider selected on the channel is never sent here. A voice request carries the channel's configuration tag and its provider parameters, and Crocotta or Pytia resolves which engine to use from that tag, matching it against the service tags configured on its own side. The provider name itself never leaves the
Channelsmodule, where it only decides which form to show and which configuration tags to offer.
Voice availability is reported to the SDKA voice capability is usable only when it is configured both on the service, here, and on the channel itself. Configuring one without the other leaves it switched off — in particular, a channel with a TTS provider selected still has no speech if this block is missing. The handshake tells the SDK which of the two are available, so a mobile app can hide its microphone and playback controls when they are not. If the app calls a voice RPC anyway, it is refused with
SPEECH_TO_TEXT_NOT_AVAILABLEorTEXT_TO_SPEECH_NOT_AVAILABLE.
Play Framework config
More information about Play Framework could be found in Play Framework configuration section
HTTP/2 has to stay enabled for the Mobile SDK gRPC API to work:
pekko.http.server.preview.enable-http2 = on
play.http.secret.keyalso signs mobile session tokensThe session token issued by the gRPC handshake is signed with the application secret. Rotating
play.http.secret.keytherefore invalidates every outstanding mobile session token, and all instances behind a load balancer must share the same secret or handshakes will not validate across them.
Job execution
This service registers three system cron jobs. They are scheduled and triggered by the cron-orchestrator, not by web-chat itself:
| Job | Schedule | Purpose |
|---|---|---|
FinishWebChatSessions | every minute | Marks sessions as expired once they pass session.max-inactivity |
CleanWebChatSessions | every hour | Permanently removes sessions older than session.delete-after |
ClearWebChatBlockingOperations | every minute | Releases stale blocking operations left behind by interrupted processing |
Troubleshooting
Nothing yet.
Updated 13 days ago
