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. On sign-out   -> DELETE /api/v1/chat/sessions?customerId=...

Endpointslink

MethodPathPurpose
POST/api/v1/chat/sessionsCreate 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 publishable widget key 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:

KeyWritten whenLives for
chat:revoked:{org}:{jti}only when you invalidate a session earlythe token's remaining lifetime
chat:live:{org}:{customerId}on every mint, to enforce the one-session rulethe 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/api/v1/chat/sessions

Get a customer's live session

Does this customer already hold a live session?

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.

Headers

x-api-key

Parameters

customerIdquerystringdefault:

Responses

200 – Live session found

{
  "sessions": [
    {
      "sessionId": "b3f1c2d4e5a67890b1c2d3e4f5a6b7c8",
      "customerId": "usr_123",
      "status": "active",
      "expiresAt": "2026-08-26T16:00:00.000Z",
      "expiresInSeconds": 21600
    }
  ]
}
boltTry it
env
GEThttps://api.newinstance.cloud/api/v1/chat/sessions

Query parameters

customerId

Headers

x-api-key

Code samples

curl -X GET 'https://api.newinstance.cloud/api/v1/chat/sessions'
POST/api/v1/chat/sessions

Create a chat session

Mint 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

FieldTypeRequiredNotes
customerIdstringYesYour stable id for this customer. Becomes the verified customer id. Max 200 characters.
customerNamestringYesName on the conversation and on every message they send. Max 200.
customerEmailstringNoAlso satisfies the merchant Require customer email setting, so the visitor is never asked. Max 320.
customerPhonestringNoShown to agents. Max 40.
metadataobjectNoExtra 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.
expiresInSecondsintegerNoYou 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.

Headers

x-api-key

Request 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

201 – Session created

{
  "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
  }
}
boltTry it
env
POSThttps://api.newinstance.cloud/api/v1/chat/sessions

Headers

x-api-key

Request body

Code samples

curl -X POST 'https://api.newinstance.cloud/api/v1/chat/sessions' \
  -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
}'
DELETE/api/v1/chat/sessions

Invalidate a session

Kill a session before it expires.

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.

Headers

x-api-key

Parameters

customerIdquerystringdefault:

Responses

200 – Session invalidated

{
  "sessionId": "b3f1c2d4e5a67890b1c2d3e4f5a6b7c8",
  "session": {
    "sessionId": "b3f1c2d4e5a67890b1c2d3e4f5a6b7c8",
    "customerId": "usr_123",
    "status": "active",
    "expiresAt": "2026-08-26T16:00:00.000Z",
    "expiresInSeconds": 21600
  },
  "revoked": true
}
boltTry it
env
DELETEhttps://api.newinstance.cloud/api/v1/chat/sessions

Query parameters

customerId

Headers

x-api-key

Code samples

curl -X DELETE 'https://api.newinstance.cloud/api/v1/chat/sessions'