Session tokens (REST API)
The REST API your backend calls to create, inspect and invalidate chat sessions.
1. Your backend -> POST /api/v1/chat/sessions (identity + lifetime)
2. Your backend <- signed JWT + session record
3. Your backend -> return the token with your own session
4. Your frontend -> hand the token to the chat SDK
5. Chat SDK -> the customer chats, already identified
6. Before expiry -> DELETE the session, POST a new one, pass it to setUserToken
7. On sign-out -> DELETE /api/v1/chat/sessions?customerId=...Endpointslink
| Method | Path | Purpose |
|---|---|---|
POST | /api/v1/chat/sessions | Create a session and get its token |
GET | /api/v1/chat/sessions?customerId= | The customer's live session, if any |
GET | /api/v1/chat/sessions/{sessionId} | Has this session been invalidated? |
DELETE | /api/v1/chat/sessions/{sessionId}?customerId= | Invalidate that session |
DELETE | /api/v1/chat/sessions?customerId= | Invalidate whatever session the customer holds |
Every endpoint requires a secret-bearing API key (x-api-key: keyId:secret) and is server-to-server only. A bare key ID is refused.
Lifetime is yours to chooselink
expiresInSeconds accepts 60 to 86400 (24 hours), defaulting to 3600. Want six-hour sessions? Send 21600. Values above the maximum are refused rather than silently clamped, so you always know what you got.
One live session per customerlink
A customer holds at most one session. POST returns 409 with the offending session while one is still valid; mint again once it expires or after you revoke it. This is deliberate: it keeps one identity from accumulating parallel credentials, and it means "the customer's session" is always a single, knowable thing.
How little is storedlink
The token is a JWT, and it is the session: the customer id, name, email, phone and metadata are all signed into it. Verifying the signature yields the whole identity, so the platform keeps no copy of any of it. Duplicating it server side would only create state that could drift from the claims.
Redis holds two things, and nothing else:
| Key | Written when | Lives for |
|---|---|---|
chat:revoked:{org}:{jti} | only when you invalidate a session early | the token's remaining lifetime |
chat:live:{org}:{customerId} | on every mint, to enforce the one-session rule | the token's lifetime |
A session that lives out its natural life is never written to the denylist at all. Both keys carry the token's own TTL, so the store empties itself and nothing has to be swept.
A denylist rather than an allowlist is deliberate: if Redis is briefly unreachable, an allowlist would reject every token and take chat down, while a denylist degrades to "an early revocation may be missed until Redis returns", bounded by the lifetime you chose. Short lifetimes make that window small, which is another reason to prefer them.
The token itself is never stored either. If you lose it, revoke the session and mint another.
Get a customer's live session
Returns the customer's live session, if they hold one.
Call it before minting to decide whether to reuse the token you have, wait, or revoke and re-mint. A customer can hold at most one session, so sessions is a list of zero or one.
Expired and revoked sessions are simply absent: the record exists only while the session is usable.
Authorization
x-api-keyAPI key · headerrequiredParameters
customerIdquery · stringdefault: Required. Your id for the customer to look up, the same customerId you minted the session with.
Responses
200Live session found401Invalid or missing API keycurl -X GET 'https://api.newinstance.cloud/api/v1/chat/sessions?customerId=usr_123' \
-H 'x-api-key: YOUR_API_KEY'{
"sessions": [
{
"sessionId": "b3f1c2d4e5a67890b1c2d3e4f5a6b7c8",
"customerId": "usr_123",
"status": "active",
"expiresAt": "2026-08-26T16:00:00.000Z",
"expiresInSeconds": 21600
}
]
}Create a chat session
Mints a chat session for one of your customers, from YOUR backend.
Call this wherever your app establishes a session (typically right after sign-in) and return the token to your frontend alongside your own session token. The frontend hands that one string to the chat SDK and passes nothing else.
Bodylink
| Field | Type | Required | Notes |
|---|---|---|---|
customerId | string | Yes | Your stable id for this customer. Becomes the verified customer id. Max 200 characters. |
customerName | string | Yes | Name on the conversation and on every message they send. Max 200. |
customerEmail | string | No | Also satisfies the merchant Require customer email setting, so the visitor is never asked. Max 320. |
customerPhone | string | No | Shown to agents. Max 40. |
metadata | object | No | Extra agent-visible context. At most 20 keys, 64-character keys, 500-character values, 4096 bytes total. Nested objects and arrays are rejected rather than flattened. |
expiresInSeconds | integer | No | You choose the lifetime: 60 to 86400 seconds (24 hours max). Defaults to 3600. The example asks for 6 hours. |
One live session per customerlink
A customer may hold one session at a time. While theirs is still valid this returns 409 with the session that is in the way. Once it expires, or you revoke it, a new one can be minted. That keeps a single identity from accumulating parallel credentials.
The 409 body carries the existing session, so you can decide whether to keep using the token you already have or revoke and re-mint.
What comes backlink
A signed JWT, and a session summary that is only an id, its owner and its expiry.
Everything about the customer is inside the token, signed. The platform keeps no copy: it would be duplicated state that could drift from the claims, and the JWT already answers the question. If you need the name, email, phone or metadata back, decode the token.
The token is never stored either. If you lose it, revoke the session and mint another.
Authorization
x-api-keyAPI key · headerrequiredRequest body
application/json{
"customerId": "usr_123",
"customerName": "Ada Lovelace",
"customerEmail": "ada@example.com",
"customerPhone": "+44 20 7946 0958",
"metadata": {
"plan": "enterprise",
"accountNumber": "AC-4471"
},
"expiresInSeconds": 21600
}Responses
201Session created400Validation error401Invalid or missing API key409Customer already has a live sessioncurl -X POST 'https://api.newinstance.cloud/api/v1/chat/sessions' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
--data-raw '{
"customerId": "usr_123",
"customerName": "Ada Lovelace",
"customerEmail": "ada@example.com",
"customerPhone": "+44 20 7946 0958",
"metadata": {
"plan": "enterprise",
"accountNumber": "AC-4471"
},
"expiresInSeconds": 21600
}'{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJiM2YxYzJkNGU1YTY3ODkwIiwic3ViIjoidXNyXzEyMyJ9.7mK2_x1s0KQ",
"expiresAt": "2026-08-26T16:00:00.000Z",
"expiresInSeconds": 21600,
"session": {
"sessionId": "b3f1c2d4e5a67890b1c2d3e4f5a6b7c8",
"customerId": "usr_123",
"status": "active",
"expiresAt": "2026-08-26T16:00:00.000Z",
"expiresInSeconds": 21600
}
}Invalidate a customer's session
Invalidates whatever session a customer holds, for sign-out when you did not keep the session id.
Idempotent: when the customer holds no session it still returns 200 with revoked: false, because "this customer has no live chat session" is the desired end state either way. Wire it into your own sign-out and you never have to track session ids.
Authorization
x-api-keyAPI key · headerrequiredParameters
customerIdquery · stringdefault: Required. Your id for the customer to sign out of chat.
Responses
200Session invalidated401Invalid or missing API keycurl -X DELETE 'https://api.newinstance.cloud/api/v1/chat/sessions?customerId=usr_123' \
-H 'x-api-key: YOUR_API_KEY'{
"session": {
"sessionId": "b3f1c2d4e5a67890b1c2d3e4f5a6b7c8",
"customerId": "usr_123",
"status": "active",
"expiresAt": "2026-08-26T16:00:00.000Z",
"expiresInSeconds": 21600
},
"revoked": true
}Get one session
Reports whether this session has been invalidated.
chatSessionId in the path is the session.sessionId returned by Create a chat session; Postman stores it after that request runs.
Invalidation is the only thing a session id can answer, and the only thing worth asking. The token is a JWT: it carries its own exp and the customer's identity, so decode it for those. What the token cannot tell you is whether someone revoked it early, which is exactly what this reports.
revoked: false means no early invalidation was recorded. It does not mean the token is unexpired, because expiry is the token's business, not the platform's.
Authorization
x-api-keyAPI key · headerrequiredParameters
chatSessionIdpath · stringrequireddefault: Responses
200Not revoked401Invalid or missing API keycurl -X GET 'https://api.newinstance.cloud/api/v1/chat/sessions/{chatSessionId}' \
-H 'x-api-key: YOUR_API_KEY'{
"sessionId": "b3f1c2d4e5a67890b1c2d3e4f5a6b7c8",
"revoked": false
}Invalidate a session
Invalidates a session before it expires.
chatSessionId in the path is the session.sessionId returned by Create a chat session.
The token stops being accepted on the next request: its signature is still valid and it has not expired, but the session is on the denylist. There is no propagation delay and no cache to wait out.
customerId is required so the customer's slot is freed as well as the token denied. Without it the token would die but the customer would stay blocked from a new session until natural expiry. You always have it: it is what you minted the session for.
Idempotent, so a retried sign-out is safe.
Authorization
x-api-keyAPI key · headerrequiredParameters
customerIdquery · stringdefault: Required. The customer this session belongs to, so their one-session slot is freed too.
chatSessionIdpath · stringrequireddefault: Responses
200Session invalidated401Invalid or missing API keycurl -X DELETE 'https://api.newinstance.cloud/api/v1/chat/sessions/{chatSessionId}?customerId=usr_123' \
-H 'x-api-key: YOUR_API_KEY'{
"sessionId": "b3f1c2d4e5a67890b1c2d3e4f5a6b7c8",
"session": {
"sessionId": "b3f1c2d4e5a67890b1c2d3e4f5a6b7c8",
"customerId": "usr_123",
"status": "active",
"expiresAt": "2026-08-26T16:00:00.000Z",
"expiresInSeconds": 21600
},
"revoked": true
}