Webchat Sessions API
Overview
The Webchat Sessions API allows your server to create customer webchat sessions and refresh session tokens on behalf of your users. This is a server-to-server API designed for backend integrations where your application manages webchat sessions programmatically — typically when you don't want the standard browser-side flow (which stores session data in localStorage) and instead want your own backend to act as the system of record.
The identity anchor for a returning user's conversation history is user.id. Sessions expire after 1 hour and the underlying session record is then deleted; what survives across sessions is the conversation, which is keyed on user.id.
To get the "reload the page, see prior chat history" behavior, you must pass a stable user.id on every call to POST /external/sessions — ideally your own internal user ID. See Preserving conversation history across sessions below.
Prerequisites
To use the APIs, you need to have a token. Tokens can be generated by an admin at the setting section in Filum CX platform. Ensure you have the necessary permissions to access the admin settings for token generation.
APIs
Access the APIs via the endpoint: https://webchat-v2.filum.ai/
POST /external/sessions
Summary
Create a webchat session for a customer.
Resolution order on every call:
- Active session match — if the user has a session that hasn't expired yet, it's reused. Match is by
user.id,user.phone, oruser.email. - Existing conversation match — if no active session exists, the server looks up a prior conversation by
user.idonly (phone/email are not used here). If found, a new session is created and attached to that conversation, so message history is preserved. - New conversation — if no prior conversation is found for that
user.id, a brand-new conversation is created (the user will see an empty chat).
This is why a stable, consistent user.id is the only reliable way to preserve conversation history across page reloads, browser changes, and the 1-hour session TTL.
Header Parameters
| Name | Description | Required | Schema |
|---|---|---|---|
| authorization | The authorization token to validate the request. The value should be in the format Bearer <your_api_token> | Yes | string |
| organization-id | The unique identifier of your organization | Yes | string |
| auth-type | The type of authorization used for the request. Must be token | Yes | string |
| content-type | Must be application/json | Yes | string |
Request Body
| Field | Type/Format | Required | Description |
|---|---|---|---|
| organizationId | string | Yes | The unique identifier of the organization |
| installedSourceId | integer | Yes | The ID of the installed source (webchat channel) |
| user | object | No | Information about the customer |
| user.id | string | No* | The unique identifier of the customer in your system. Strongly recommended. If omitted, the server generates a random Webchat-<uuid> and returns it as userId in the response — you then become responsible for storing it and echoing it back as user.id on subsequent calls, otherwise the user will get a fresh empty conversation on every reload. See Preserving conversation history across sessions. |
| user.name | string | No | The name of the customer |
| user.email | string | No | The email of the customer |
| user.phone | string | No | The phone number of the customer |
Request samples
{
"organizationId": "org_12345",
"installedSourceId": 1,
"user": {
"id": "customer_abc123",
"name": "John Doe",
"email": "john.doe@example.com",
"phone": "+84901234567"
}
}
Responses
| Code | Description | Content type |
|---|---|---|
| 201 | Successful Response | application/json |
| 401 | Unauthorized | application/json |
| 422 | Validation Error | application/json |
| 429 | Too Many Requests | application/json |
201 Successful Response
| Field | Type/Format | Required | Description |
|---|---|---|---|
| userId | string | Yes | The user ID for the session (provided or auto-generated) |
| sessionId | string | Yes | The unique identifier of the session |
| sessionToken | string | Yes | A JWT token for the session (expires in 1 hour) |
| sessionTokenExpiration | integer | Yes | The expiration timestamp of the session token (Unix epoch in seconds) |
| websocketUrl | string | Yes | The WebSocket URL for real-time messaging |
| conversationId | string | Yes | The unique identifier of the conversation |
Response samples
{
"userId": "customer_abc123",
"sessionId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"sessionToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"sessionTokenExpiration": 1705315800,
"websocketUrl": "wss://webchat-ws.filum.ai/a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"conversationId": "507f1f77bcf86cd799439011"
}
POST /external/sessions/refresh
Summary
Refresh an existing session token. This extends the session expiration by 1 hour and returns a new JWT token. Use this to keep a session alive without creating a new one.
Sessions expire 1 hour after they were last created or refreshed, and the session record is deleted shortly after. Once that happens, this endpoint returns 404 Session not found and cannot be used to recover the user's chat. To restore the user's prior conversation after that point, you must call POST /external/sessions again with the same user.id you originally used — that is what re-attaches the new session to the existing conversation. Storing only sessionId in your middleware is not enough.
Header Parameters
| Name | Description | Required | Schema |
|---|---|---|---|
| authorization | The authorization token to validate the request. The value should be in the format Bearer <your_api_token> | Yes | string |
| organization-id | The unique identifier of your organization | Yes | string |
| auth-type | The type of authorization used for the request. Must be token | Yes | string |
| content-type | Must be application/json | Yes | string |
Request Body
| Field | Type/Format | Required | Description |
|---|---|---|---|
| sessionId | string | Yes | The unique identifier of the session to refresh |
sessionId in your backendAlthough this endpoint accepts only sessionId, that is not enough state to keep around for a returning user. The moment this endpoint returns 404 Session not found (which it will, 1 hour after the last refresh), your only way back to the user's chat history is to call POST /external/sessions with the same user.id you used originally.
For every webchat user, your middleware should persist at minimum:
userId(from the create-session response) — or your own internal user ID if you passed it asuser.id. This is the identity anchor; losing it loses the conversation.sessionId— only useful while the session is still alive.
Do not treat sessionId as a long-lived handle. Treat user.id as the long-lived handle and sessionId as a short-lived (≤1 hour) token derived from it.
Request samples
What your backend must have persisted for the user (carried over from the create-session response):
{
// Long-lived identity handle. Required to ever recover this user's
// conversation history after the session expires. NEVER drop this.
"userId": "customer_abc123",
// Short-lived (≤1 hour). Only valid until the next failed refresh.
"sessionId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
The actual POST /external/sessions/refresh request body — note that userId is not sent on the wire, but you still must have it stored, because when this call eventually returns 404 you will need it to call POST /external/sessions and reattach to the existing conversation:
{
"sessionId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
Responses
| Code | Description | Content type |
|---|---|---|
| 200 | Successful Response | application/json |
| 401 | Unauthorized | application/json |
| 404 | Session Not Found | application/json |
| 422 | Validation Error | application/json |
| 429 | Too Many Requests | application/json |
200 Successful Response
| Field | Type/Format | Required | Description |
|---|---|---|---|
| sessionToken | string | Yes | A new JWT token for the session (expires in 1 hour) |
| sessionTokenExpiration | integer | Yes | The expiration timestamp of the new session token (Unix epoch in seconds) |
Response samples
{
"sessionToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"sessionTokenExpiration": 1705319400
}
Usage Examples
Example 1: Create a session for a known customer
curl -X POST "https://webchat-v2.filum.ai/external/sessions" \
-H "authorization: Bearer your_api_token_here" \
-H "organization-id: your_organization_id_here" \
-H "auth-type: token" \
-H "Content-Type: application/json" \
-d '{
"organizationId": "org_12345",
"installedSourceId": 1,
"user": {
"id": "customer_abc123",
"name": "John Doe",
"email": "john.doe@example.com",
"phone": "+84901234567"
}
}'
Example 2: Create a session for an anonymous visitor
Omitting user.id is only appropriate for truly one-off, throwaway sessions. The server will generate a random userId and return it in the response — if you don't store that value and pass it back as user.id on the user's next visit, they will see a fresh empty chat every time. For authenticated users on your side, always send your own stable user ID as user.id instead.
curl -X POST "https://webchat-v2.filum.ai/external/sessions" \
-H "authorization: Bearer your_api_token_here" \
-H "organization-id: your_organization_id_here" \
-H "auth-type: token" \
-H "Content-Type: application/json" \
-d '{
"organizationId": "org_12345",
"installedSourceId": 1
}'
Example 3: Refresh a session token
curl -X POST "https://webchat-v2.filum.ai/external/sessions/refresh" \
-H "authorization: Bearer your_api_token_here" \
-H "organization-id: your_organization_id_here" \
-H "auth-type: token" \
-H "Content-Type: application/json" \
-d '{
"sessionId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}'
Preserving conversation history across sessions
If you're integrating /external/* because you don't want the webchat to use browser localStorage — i.e. your own backend is the system of record for the user's webchat identity — this section is the contract you need to follow.
The model
- A conversation is the durable thing — it holds the entire message history.
- A session is a 1-hour bearer credential over a conversation. It expires and is deleted automatically.
- The server links a new session back to the prior conversation by matching
user.idinPOST /external/sessions. Nothing else (notemail, notphone, notsessionIdafter expiry) is used as a fallback.
So: across reloads, browsers, devices, and after session expiry, the conversation persists only if user.id is the same.
Recommended integration (you have your own auth)
This is the path that "just works" — no extra storage required on your side.
- On every page load where the user is authenticated, call
POST /external/sessionswithuser.idset to your own internal user ID (the same ID you use everywhere else in your system). - Use the returned
sessionTokento drive the webchat for the next hour. - If you want to keep the session alive past 1 hour without forcing the user to reload, call
POST /external/sessions/refreshwith thesessionIdbefore the token expires. - When the user logs out of your app, simply stop using the session — there's nothing to clean up.
With this pattern you do not need to persist sessionId, sessionToken, or userId in your backend. You can re-derive everything from your own user ID at any time.
Alternative integration (you must use the server-generated ID)
If you can't pass your own ID for some reason and let the server generate Webchat-<uuid> on the first call:
- First call: omit
user.id. The response containsuserId(e.g.Webchat-abc123…). - Persist that
userIdin your backend, keyed to the user. This is now your only handle on their conversation history. - On every subsequent call to
POST /external/sessionsfor the same user, setuser.idto that stored value. - If you lose that value, you lose access to the prior conversation — there is no recovery path.
Common mistakes that produce "blank chat on reload"
- Calling
POST /external/sessionswith onlyemail/phone/nameand nouser.id. The server generates a new randomuserIdon every such call → a new conversation each time. - Storing only
sessionIdin your middleware and relying on/external/sessions/refresh. Works for 1 hour, then404s, and your fallbackPOST /external/sessionshas nouser.idto recover from. - Sending different
user.idshapes between calls (e.g. omitted the first time, then your own ID later). The server can't link them. - Assuming
emailorphonewill be used to recover history. They won't — they only match against currently active (unexpired) sessions, not historical conversations.
Quick decision table
| What you store / send | History survives reload within 1 hour | History survives reload after 1 hour |
|---|---|---|
Your own user ID as user.id every call | Yes | Yes |
Server-generated userId, echoed as user.id every call | Yes | Yes |
Only sessionId, refreshed periodically | Yes (via refresh) | No |
Only email / phone, no user.id | Yes (same active session matched) | No |
| Nothing — recreate fresh each visit | No | No |
Rate Limiting
The API endpoints have generous rate limits designed for server-to-server usage:
- Per second: 20 requests (configurable via
EXTERNAL_WEBCHAT_PER_SEC_LIMIT) - Per minute: 1,200 requests (configurable via
EXTERNAL_WEBCHAT_PER_MIN_LIMIT)
If you exceed these limits, you will receive a 429 Too Many Requests response. Please implement appropriate retry logic with exponential backoff in your integration.
Error Responses
401 Unauthorized
Returned when the authorization token is missing, invalid, or the token does not have the required permissions.
{
"message": "Unauthorized",
"statusCode": 401
}
404 Not Found
Returned when the session ID does not exist (refresh endpoint only).
{
"message": "Session not found",
"statusCode": 404
}
422 Validation Error
Returned when the request body is missing required fields or contains invalid data.
{
"message": ["organizationId should not be empty", "organizationId must be a string"],
"error": "Bad Request",
"statusCode": 400
}
429 Too Many Requests
{
"statusCode": 429,
"message": "ThrottlerException: Too Many Requests"
}