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 client_context, and list/inspect
the resulting session records. Get IBLAI_ORG/IBLAI_USERNAME/IBLAI_API_KEY
from /iblai-api-login.
Auth & conventions
- Header:
Authorization: Api-Token $IBLAI_API_KEY on every request.
- Path vars:
{org} = $IBLAI_ORG, {user} = $IBLAI_USERNAME.
- 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 → client_context 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["client_context"]. It is
session-level: each turn's metadata overwrites the session's client_context, 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 page_content is
also appended to the prompt, but — unlike metadata — is stripped before the message is
saved.
document_filter → retrieval scoping (hard). An optional
BaseConsumerPayload.document_filter object ({key: scalar}) restricts RAG retrieval to
documents whose ingested custom_metadata 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,
document_filter 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 client_context, read from
the session's metadata["client_context"].
- GET
…/orgs/{org}/users/{user}/sessions/{session_id}/tasks/{task_id}/ — the
chat-history export (DownloadableChatHistory); every item carries a client_context
field. Add ?to_csv=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/?platform_key={org}&session_id={session_id}
— documented under /iblai-api-analytics.
Writes
- POST
https://asgi.data.iblai.app/api/agent/chat/?platform_key={org}&session_id={session_id}
— send a chat turn; response is Server-Sent Events. Body (BaseConsumerPayload):{
"session_id": "…",
"prompt": "Hello",
"flow": { "name": "<agent unique_id>", "tenant": "<org key>" },
"page_content": "optional text appended to the prompt, stripped before saving",
"metadata": { "any": "soft client context keys" },
"document_filter": { "stateCode": "CA" }
}
session_id (a UUID4) and flow are required; flow.name selects the agent (its
unique_id, or a slug/name) and flow.tenant is the org key. prompt and
page_content default to empty, metadata and document_filter to null. metadata is
soft passthrough — stored on the session as client_context (see Concepts) and echoed in
the reads above. document_filter 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. session_id and flow are required on every turn, and flow.name must
resolve to a deployed agent (its unique_id, 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 / client_context (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["client_context"] and read back as client_context in
the session-messages response, the history export (a client_context field per item, or
CSV column via ?to_csv=true), and analytics summary.client_context. It is appended to
the prompt but never restricts retrieval.
document_filter (hard) — an optional JSON object (dict[str, str|int|float|bool] | null, BaseConsumerPayload.document_filter) that scopes RAG retrieval to documents whose
ingested custom_metadata 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
custom_metadata 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 — 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
client_context vs. the per-message snapshot), and the soft metadata vs. hard
document_filter comparison with the state-specific + generic retrieval pattern.
1---2name: iblai-api-agent-session3description: Talk to a deployed ibl.ai agent directly over REST/SSE (or WebSocket) and manage its chat sessions — POST a prompt to the agent chat endpoint, attach arbitrary metadata (surfaced later as client_context), and list/read sessions and per-task history exports. The direct-transport counterpart to iblai-api-agent-chat's MCP wiring; use when you want raw streamed chat + session records rather than an MCP server.4---56# iblai-api-agent-session78Drive a deployed agent's **chat transport directly** and read its **sessions**.9Where `/iblai-api-agent-chat` wires a hosted MCP server for conversation, this10skill is the raw REST/SSE (and WebSocket) surface: POST a prompt, stream the11reply, attach `metadata` that resurfaces as `client_context`, and list/inspect12the resulting session records. Get `IBLAI_ORG`/`IBLAI_USERNAME`/`IBLAI_API_KEY`13from `/iblai-api-login`.1415## Auth & conventions1617- **Header:** `Authorization: Api-Token $IBLAI_API_KEY` on every request.18- **Path vars:** `{org}` = `$IBLAI_ORG`, `{user}` = `$IBLAI_USERNAME`.19- **Two hosts — chat is streaming/ASGI:**20 - Chat turn (SSE / WebSocket) → `https://asgi.data.iblai.app`21 - Session reads/writes → `https://api.iblai.app/dm/api/ai-mentor/orgs/{org}/users/{user}/v1` … i.e. `…/orgs/{org}/users/{user}/sessions/…`22- Not connected yet? Run **`/iblai-api-login`** first.2324## Concepts2526**Two independent context fields — one soft, one hard.** A chat turn can carry both27`metadata` (soft) and `document_filter` (hard); they do different jobs and don't28substitute for each other:2930- **`metadata` (soft)** — steers *how the agent reasons*. It's appended to the prompt and31 persisted; it never restricts which documents RAG can retrieve.32- **`document_filter` (hard)** — steers *which documents RAG may retrieve*. It's a33 document-level allow-list applied in the vector store; it does not touch the prompt.3435Passing `metadata` alone narrows nothing in retrieval; passing `document_filter` alone36scopes retrieval without giving the agent any extra prose context. Use both when you want37both effects.3839**`metadata` → `client_context` passthrough (soft).** Every chat turn (WS or SSE) may40carry a `metadata` object of arbitrary key/values (`BaseConsumerPayload.metadata`). The41runner folds it into the prompt the agent sees, so the agent can tailor its reply, and the42consumer persists it on the session as `Session.metadata["client_context"]`. It is43**session-level**: each turn's `metadata` overwrites the session's `client_context`, so44it sticks across turns until you send new keys. Use it to tell one deployed agent45*where/why* a message arrives (product, plan tier, page, region) without editing its46prompt. It then echoes back on every read below. The sibling field `page_content` is47also appended to the prompt, but — unlike `metadata` — is stripped before the message is48saved.4950**`document_filter` → retrieval scoping (hard).** An optional51`BaseConsumerPayload.document_filter` object (`{key: scalar}`) restricts RAG retrieval to52documents whose ingested `custom_metadata` is compatible with the filter. It is matched53**inclusively per key**: a document is kept if, for **every** filter key, it either54*matches that key's value* **or** *does not carry that key at all*; it is excluded only55when it carries the key with a **different** value. Multiple keys are AND'd. Because a56document that lacks a key is never excluded by it, generic/untagged material always57survives — e.g. `{"stateCode":"CA"}` keeps California docs **and** generic docs that carry58no `stateCode`, while dropping docs tagged for other states. Unlike `metadata`,59`document_filter` is **not** session-sticky — it applies only to the turn that sends it —60and it never appears in the prompt or saved history. It only shrinks the retrieval61candidate set; top-k similarity ranking still runs afterward, so an eligible document is62not guaranteed to be retrieved if higher-scoring eligible documents fill the top-k.6364## Reads6566- **GET** `…/dm/api/ai-mentor/orgs/{org}/users/{user}/sessions/` — list the user's67 chat sessions.68- **GET** `…/orgs/{org}/users/{user}/sessions/{session_id}/` — the session's paginated69 chat messages (`MessageView`); the response also carries `client_context`, read from70 the session's `metadata["client_context"]`.71- **GET** `…/orgs/{org}/users/{user}/sessions/{session_id}/tasks/{task_id}/` — the72 chat-history export (`DownloadableChatHistory`); every item carries a `client_context`73 field. Add `?to_csv=true` for a CSV whose columns are exactly74 `type,content,timestamp,client_context`. Kicking off (POST) and polling that export75 task is owned by **`/iblai-api-agent-history`**.76- **Analytics echo:** the same value comes back at `summary.client_context` from77 **GET** `…/dm/api/analytics/messages/details/?platform_key={org}&session_id={session_id}`78 — documented under **`/iblai-api-analytics`**.7980## Writes8182- **POST** `https://asgi.data.iblai.app/api/agent/chat/?platform_key={org}&session_id={session_id}`83 — send a chat turn; response is Server-Sent Events. Body (`BaseConsumerPayload`):84 ```json85 {86 "session_id": "…",87 "prompt": "Hello",88 "flow": { "name": "<agent unique_id>", "tenant": "<org key>" },89 "page_content": "optional text appended to the prompt, stripped before saving",90 "metadata": { "any": "soft client context keys" },91 "document_filter": { "stateCode": "CA" }92 }93 ```94 `session_id` (a UUID4) and `flow` are **required**; `flow.name` selects the agent (its95 `unique_id`, or a slug/name) and `flow.tenant` is the org key. `prompt` and96 `page_content` default to empty, `metadata` and `document_filter` to null. `metadata` is97 soft passthrough — stored on the session as `client_context` (see Concepts) and echoed in98 the reads above. `document_filter` is the hard retrieval scope — an inclusive per-key99 allow-list over documents' ingested `custom_metadata`, applied only to this turn and never100 persisted (see Concepts and Schema). The **same payload** works over WebSocket at101 `wss://asgi.data.iblai.app/ws/chat/`.102- **POST** `…/dm/api/ai-mentor/orgs/{org}/users/{user}/sessions/` — create/retrieve a103 session (`ChatSessionView`; the body's `mentor` field picks the agent). Or let the104 first chat turn create one by passing a new `session_id`.105106## Example107108```bash109# Stream a chat turn with attached client context (SSE). MENTOR = the agent's unique_id.110curl -N -X POST \111 "https://asgi.data.iblai.app/api/agent/chat/?platform_key=$IBLAI_ORG&session_id=$SESSION" \112 -H "Authorization: Api-Token $IBLAI_API_KEY" \113 -H "Content-Type: application/json" \114 -H "Accept: text/event-stream" \115 -d '{"session_id":"'"$SESSION"'","prompt":"Summarize my notes","flow":{"name":"'"$MENTOR"'","tenant":"'"$IBLAI_ORG"'"},"metadata":{"source":"docs","tab":"notes"}}'116117# Read the session's messages back (client_context is the metadata you sent)118curl "https://api.iblai.app/dm/api/ai-mentor/orgs/$IBLAI_ORG/users/$IBLAI_USERNAME/sessions/$SESSION/" \119 -H "Authorization: Api-Token $IBLAI_API_KEY"120121# Download the history export as CSV (client_context is a column)122curl "https://api.iblai.app/dm/api/ai-mentor/orgs/$IBLAI_ORG/users/$IBLAI_USERNAME/sessions/$SESSION/tasks/$TASK/?to_csv=true" \123 -H "Authorization: Api-Token $IBLAI_API_KEY"124```125126## Notes127128- **Streaming runs on ASGI** — the chat turn (`/api/agent/chat/`, `/ws/chat/`) is on129 `asgi.data.iblai.app`; session reads are ordinary REST on the `api.iblai.app/dm`130 gateway. `session_id` and `flow` are required on every turn, and `flow.name` must131 resolve to a deployed agent (its `unique_id`, slug, or name).132- For **OpenAI-format** inference against a provider/model (no agent RAG/memory), use133 `/iblai-api-inference`; to chat via an **MCP server** instead of raw SSE, use134 `/iblai-api-agent-chat`; the history-export task (kick off + poll) is135 `/iblai-api-agent-history`; the `summary.client_context` analytics read is136 `/iblai-api-analytics`.137138## Schema139140**`metadata` / `client_context`** (soft) — an arbitrary JSON object (`dict[str, Any] |141null`, `BaseConsumerPayload.metadata`). No fixed keys; use whatever your app needs, e.g.142`product`, `planTier`, `userRole`, `region`. Sent as `metadata` on a chat turn, it is143persisted at `Session.metadata["client_context"]` and read back as `client_context` in144the session-messages response, the history export (a `client_context` field per item, or145CSV column via `?to_csv=true`), and analytics `summary.client_context`. It is appended to146the prompt but never restricts retrieval.147148**`document_filter`** (hard) — an optional JSON object (`dict[str, str|int|float|bool] |149null`, `BaseConsumerPayload.document_filter`) that scopes RAG retrieval to documents whose150ingested `custom_metadata` is compatible with the filter. It never touches the prompt and151is **not** persisted on the session (per-turn only). The keys/values here match the152`custom_metadata` you attach when adding documents — see `/iblai-api-agent-dataset` for153tagging documents at ingestion (the `custom_metadata` field on `documents/train/`).154155- **Keys** must be flat and alphanumeric/underscore (`^\w+$`) — no `__`, no ORM-style156 lookup suffixes (`__icontains`, etc.). **Values** must be scalars (string, number, or157 boolean); lists/objects/null are rejected. A malformed filter fails the turn with a158 validation error rather than being silently ignored.159- **Inclusive per key:** a document is kept if, for **every** filter key, it *matches the160 value* **or** *does not carry that key*; it is excluded only when it carries the key with161 a **different** value. Multiple keys are AND'd.162- **Matching is exact** — value comparison is case- and type-sensitive (`"CA"` ≠ `"ca"`;163 the integer `2026` ≠ the string `"2026"`), and keys are matched exactly (`"stateCode"` ≠164 `"statecode"`); a key no document carries acts as a no-op for that key.165- **State + generic pattern:** ingest generic/shared documents with **no** state key and166 state-specific documents with `stateCode = <state>`; then `{"stateCode":"CA"}` retrieves167 California **and** generic documents while excluding other states — no per-state agent or168 two-stage retrieval needed. To search everything, send **no** `document_filter` (an empty169 or fully non-matching filter can yield an empty candidate set, and the agent may then170 answer without any retrieved context).171- **Eligibility ≠ retrieval:** the filter only shrinks the candidate set; top-k similarity172 ranking still runs, so an eligible document is not guaranteed to surface if higher-scoring173 eligible documents fill the top-k. Raise the agent's retrieval `k` if broad generic174 material is being crowded out.175176## Reference material177178- [`references/metadata-passthrough.md`](references/metadata-passthrough.md) — the179 `metadata` pass-through companion: the `<CONTEXT METADATA>` prompt-injection format,180 per-transport wire notes (SSE/WebSocket + the embedded-iframe `postMessage` channel),181 session caching (send-once, replace-not-merge, ~2h TTL), the182 one-agent-many-contexts pattern, the storage/pipeline map (session183 `client_context` vs. the per-message snapshot), and the **soft `metadata` vs. hard184 `document_filter`** comparison with the state-specific + generic retrieval pattern.