iblai-api-agent-session
Drive a deployed agent's chat transport directly and read its sessions. Where /iblai-api-agent-chat wires a hosted MCP server for conversation, this skill is the raw REST/SSE (and WebSocket) surface: POST a prompt, stream the reply, attach metadata that resurfaces as clientcontext, and list/inspect the resulting session records. Get IBLAIORG/IBLAIUSERNAME/IBLAIAPI_KEY from /iblai-api-login.
Auth & conventions
- Header:
Authorization: Api-Token $IBLAIAPIKEY on every request.
- Path vars:
{org} = $IBLAIORG, {user} = $IBLAIUSERNAME.
- Two hosts — chat is streaming/ASGI:
- Chat turn (SSE / WebSocket) → https://asgi.data.iblai.app - Session reads/writes → https://api.iblai.app/dm/api/ai-mentor/orgs/{org}/users/{user}/v1 … i.e. …/orgs/{org}/users/{user}/sessions/…
- Not connected yet? Run
/iblai-api-login first.
Concepts
Two independent context fields — one soft, one hard. A chat turn can carry both metadata (soft) and document_filter (hard); they do different jobs and don't substitute for each other:
metadata (soft) — steers how the agent reasons. It's appended to the prompt and
persisted; it never restricts which documents RAG can retrieve.
document_filter (hard) — steers which documents RAG may retrieve. It's a
document-level allow-list applied in the vector store; it does not touch the prompt.
Passing metadata alone narrows nothing in retrieval; passing document_filter alone scopes retrieval without giving the agent any extra prose context. Use both when you want both effects.
metadata → clientcontext passthrough (soft). Every chat turn (WS or SSE) may carry a metadata object of arbitrary key/values (BaseConsumerPayload.metadata). The runner folds it into the prompt the agent sees, so the agent can tailor its reply, and the consumer persists it on the session as Session.metadata["clientcontext"]. It is session-level: each turn's metadata overwrites the session's clientcontext, so it sticks across turns until you send new keys. Use it to tell one deployed agent where/why a message arrives (product, plan tier, page, region) without editing its prompt. It then echoes back on every read below. The sibling field pagecontent is also appended to the prompt, but — unlike metadata — is stripped before the message is saved.
documentfilter → retrieval scoping (hard). An optional BaseConsumerPayload.documentfilter object ({key: scalar}) restricts RAG retrieval to documents whose ingested custommetadata is compatible with the filter. It is matched inclusively per key: a document is kept if, for every filter key, it either matches that key's value or does not carry that key at all; it is excluded only when it carries the key with a different value. Multiple keys are AND'd. Because a document that lacks a key is never excluded by it, generic/untagged material always survives — e.g. {"stateCode":"CA"} keeps California docs and generic docs that carry no stateCode, while dropping docs tagged for other states. Unlike metadata, documentfilter is not session-sticky — it applies only to the turn that sends it — and it never appears in the prompt or saved history. It only shrinks the retrieval candidate set; top-k similarity ranking still runs afterward, so an eligible document is not guaranteed to be retrieved if higher-scoring eligible documents fill the top-k.
Reads
- GET
…/dm/api/ai-mentor/orgs/{org}/users/{user}/sessions/ — list the user's
chat sessions.
- GET
…/orgs/{org}/users/{user}/sessions/{session_id}/ — the session's paginated
chat messages (MessageView); the response also carries clientcontext, read from the session's metadata["clientcontext"].
- GET
…/orgs/{org}/users/{user}/sessions/{sessionid}/tasks/{taskid}/ — the
chat-history export (DownloadableChatHistory); every item carries a clientcontext field. Add ?tocsv=true for a CSV whose columns are exactly type,content,timestamp,client_context. Kicking off (POST) and polling that export task is owned by /iblai-api-agent-history.
- Analytics echo: the same value comes back at
summary.client_context from
GET …/dm/api/analytics/messages/details/?platformkey={org}&sessionid={session_id} — documented under /iblai-api-analytics.
Writes
- POST
https://asgi.data.iblai.app/api/agent/chat/?platformkey={org}&sessionid={session_id}
— send a chat turn; response is Server-Sent Events. Body (BaseConsumerPayload): ``json { "sessionid": "…", "prompt": "Hello", "flow": { "name": "<agent uniqueid>", "tenant": "<org key>" }, "pagecontent": "optional text appended to the prompt, stripped before saving", "metadata": { "any": "soft client context keys" }, "documentfilter": { "stateCode": "CA" } } ` sessionid (a UUID4) and flow are required; flow.name selects the agent (its uniqueid, or a slug/name) and flow.tenant is the org key. prompt and pagecontent default to empty, metadata and documentfilter to null. metadata is soft passthrough — stored on the session as clientcontext (see Concepts) and echoed in the reads above. documentfilter is the hard retrieval scope — an inclusive per-key allow-list over documents' ingested custom_metadata, applied only to this turn and never persisted (see Concepts and Schema). The same payload works over WebSocket at wss://asgi.data.iblai.app/ws/chat/`.
- POST
…/dm/api/ai-mentor/orgs/{org}/users/{user}/sessions/ — create/retrieve a
session (ChatSessionView; the body's mentor field picks the agent). Or let the first chat turn create one by passing a new session_id.
Example
# Stream a chat turn with attached client context (SSE). MENTOR = the agent's unique_id.
curl -N -X POST \
"https://asgi.data.iblai.app/api/agent/chat/?platform_key=$IBLAI_ORG&session_id=$SESSION" \
-H "Authorization: Api-Token $IBLAI_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
-d '{"session_id":"'"$SESSION"'","prompt":"Summarize my notes","flow":{"name":"'"$MENTOR"'","tenant":"'"$IBLAI_ORG"'"},"metadata":{"source":"docs","tab":"notes"}}'
# Read the session's messages back (client_context is the metadata you sent)
curl "https://api.iblai.app/dm/api/ai-mentor/orgs/$IBLAI_ORG/users/$IBLAI_USERNAME/sessions/$SESSION/" \
-H "Authorization: Api-Token $IBLAI_API_KEY"
# Download the history export as CSV (client_context is a column)
curl "https://api.iblai.app/dm/api/ai-mentor/orgs/$IBLAI_ORG/users/$IBLAI_USERNAME/sessions/$SESSION/tasks/$TASK/?to_csv=true" \
-H "Authorization: Api-Token $IBLAI_API_KEY"
Notes
- Streaming runs on ASGI — the chat turn (
/api/agent/chat/, /ws/chat/) is on
asgi.data.iblai.app; session reads are ordinary REST on the api.iblai.app/dm gateway. sessionid and flow are required on every turn, and flow.name must resolve to a deployed agent (its uniqueid, slug, or name).
- For OpenAI-format inference against a provider/model (no agent RAG/memory), use
/iblai-api-inference; to chat via an MCP server instead of raw SSE, use /iblai-api-agent-chat; the history-export task (kick off + poll) is /iblai-api-agent-history; the summary.client_context analytics read is /iblai-api-analytics.
Schema
metadata / clientcontext (soft) — an arbitrary JSON object (dict[str, Any] | null, BaseConsumerPayload.metadata). No fixed keys; use whatever your app needs, e.g. product, planTier, userRole, region. Sent as metadata on a chat turn, it is persisted at Session.metadata["clientcontext"] and read back as clientcontext in the session-messages response, the history export (a clientcontext field per item, or CSV column via ?tocsv=true), and analytics summary.clientcontext. It is appended to the prompt but never restricts retrieval.
documentfilter (hard) — an optional JSON object (dict[str, str|int|float|bool] | null, BaseConsumerPayload.documentfilter) that scopes RAG retrieval to documents whose ingested custommetadata is compatible with the filter. It never touches the prompt and is not persisted on the session (per-turn only). The keys/values here match the custommetadata you attach when adding documents — see /iblai-api-agent-dataset for tagging documents at ingestion (the custom_metadata field on documents/train/).
- Keys must be flat and alphanumeric/underscore (
^\w+$) — no __, no ORM-style
lookup suffixes (__icontains, etc.). Values must be scalars (string, number, or boolean); lists/objects/null are rejected. A malformed filter fails the turn with a validation error rather than being silently ignored.
- Inclusive per key: a document is kept if, for every filter key, it *matches the
value or does not carry that key*; it is excluded only when it carries the key with a different value. Multiple keys are AND'd.
- Matching is exact — value comparison is case- and type-sensitive (
"CA" ≠ "ca";
the integer 2026 ≠ the string "2026"), and keys are matched exactly ("stateCode" ≠ "statecode"); a key no document carries acts as a no-op for that key.
- State + generic pattern: ingest generic/shared documents with no state key and
state-specific documents with stateCode = <state>; then {"stateCode":"CA"} retrieves California and generic documents while excluding other states — no per-state agent or two-stage retrieval needed. To search everything, send no document_filter (an empty or fully non-matching filter can yield an empty candidate set, and the agent may then answer without any retrieved context).
- Eligibility ≠ retrieval: the filter only shrinks the candidate set; top-k similarity
ranking still runs, so an eligible document is not guaranteed to surface if higher-scoring eligible documents fill the top-k. Raise the agent's retrieval k if broad generic material is being crowded out.
Reference material
- [
references/metadata-passthrough.md](references/metadata-passthrough.md) — the
metadata pass-through companion: the <CONTEXT METADATA> prompt-injection format, per-transport wire notes (SSE/WebSocket + the embedded-iframe postMessage channel), session caching (send-once, replace-not-merge, ~2h TTL), the one-agent-many-contexts pattern, the storage/pipeline map (session clientcontext vs. the per-message snapshot), and the soft metadata vs. hard documentfilter comparison with the state-specific + generic retrieval pattern.