The idea in one sentence
You hold the conversation. We hold the signature.Tuteliq’s safety endpoints get sharper when they can see the conversation arc — not just one message. Most platforms solve this by storing the conversation server-side. Tuteliq doesn’t. We return a signed, opaque token containing the derived analysis state (turn count, accumulated categories, severity trajectory, recommended actions). You hold the token; on the next call, you pass it back. We never store a single message of yours.
Why it matters
How it works
The token is a signed JWT (HS256) withtyp: "TLQT" (Tuteliq Continuation Token). It’s opaque to you — you don’t decode it, you just round-trip it.
Supported endpoints
Token is single-endpoint by design — a token issued by
/safety/grooming cannot be used on /safety/bullying. Each conversation type maintains its own state.
Quick start
First call (seeds the token)
Subsequent calls (pass it back)
Request fields
Response fields
Privacy properties
These hold by construction, not by policy:- No raw text in the token — only derived signals (counts, confidences, severity trajectory)
- No PII in the token — emails, phone numbers, addresses are never carried forward
- Per-key binding — a token issued to your key cannot be used by another customer’s key (HMAC
subclaim binds to a hash of your API key) - Per-endpoint binding — tokens cannot cross endpoints
- Tamper-evident — any modification invalidates the HMAC signature
- Bounded lifetime — 24h default expiry
- A leaked token reveals nothing beyond what’s already in the public API response for that conversation
Errors
When a token can’t be used, you’ll get a structured error:
In every case, the fix is the same: drop the token and start a fresh conversation.
Best practices
- Persist the token per conversation, not per user. A user with three open chats has three tokens.
- Don’t log it server-side if you can avoid it — treat it like a session cookie. It’s signed but it’s also small and bounded; the less it sits around, the better.
- On any error code, gracefully fall back. Send the recent conversation_history once to re-seed, then continue with the new token.
- Use
reset_conversation: trueto explicitly close a conversation arc and start fresh (e.g., end of a support session). - Use
message_idwith your own message identifiers — the token’s evidence pointers will reference them so you can map detections back to your records.
Coexistence with conversation_history
The conversation_history field (today’s pattern) still works. Three modes:
- History only (today) — full conversation each call, server is stateless, no token returned… actually the new response does include a token you can adopt going forward.
- Token only (new) — only the new turn, plus the token. Privacy-first.
- Both — token wins; history is ignored on this call. Use this only for one-call migration.
Grooming-endpoint legacy session_id
POST /v1/safety/grooming historically supported a server-stored session_id. That endpoint still works but is scheduled for deprecation — the continuation token offers the same multi-turn awareness without server-side storage. New integrations should use continuation_token; existing integrations have a clean migration path (send continuation_token instead of session_id; response shapes are unchanged otherwise).
Token shape (for the curious)
- Header —
{ "alg": "HS256", "typ": "TLQT", "kid": "tlq-ct-YYYY-MM" } - Payload — derived state:
{ v, ep, sub, lang, ac, tc, cat, ev, sev_hist, trj, rec, iat, exp } - Signature — HMAC-SHA256 over
header.payloadwith the secret bound tokid