ReflexioDeveloper Docs
Menu
All

Interaction Management

Methods for publishing, searching, retrieving, and deleting user interactions.

publish_interaction

Publish user interactions to the system. This is the primary method for sending data to Reflexio.

Async applications can call await client.publish_interaction_async(...) with the same parameters and return type. It uses native async HTTP, applies the client's total timeout, and preserves task cancellation.

response = client.publish_interaction(
    user_id,
    interactions,
    session_id="session_abc",
    source="",
    agent_version="agent-v0",
    wait_for_response=False,
    skip_aggregation=False,
    force_extraction=False,
    evaluation_only=False,
    override_learning_stall=False,
    retrieval_experiment_id=None,
    retrieval_experiment_arm=None,
)
curl -X POST "${REFLEXIO_URL:-https://www.reflexio.ai}/api/publish_interaction" \
  -H "User-Agent: my-agent-reflexio" \
  -H "Authorization: Bearer $REFLEXIO_API_KEY" \
  -H "Content-Type: application/json" \
  --data @- <<'JSON'
{
  "user_id": "<user_id>",
  "interaction_data_list": "<interactions>",
  "session_id": "session_abc",
  "source": "",
  "agent_version": "agent-v0",
  "skip_aggregation": false,
  "force_extraction": false,
  "evaluation_only": false,
  "override_learning_stall": false,
  "retrieval_experiment_id": null,
  "retrieval_experiment_arm": null
}
JSON

Report the Reflexio context used by each response

If an agent response was produced with profiles or playbooks retrieved from Reflexio, add every injected learning to that response's retrieved_learnings. When the session is evaluated, Reflexio can then show whether each learning was relevant and whether its impact was positive, neutral, or negative. This attribution also provides the signal needed to improve future optimization of retrieved learnings. Omit the field only when no Reflexio learning was injected.

Prop

Type

Publish retrieval experiment attribution

When a learning-search response includes experiment, echo its experiment_id and arm on every related publish. Both treatment and holdout publish normally; do not set evaluation_only=True merely because the user is in holdout. Omit both fields when the search response has no experiment.

Returns: PublishUserInteractionResponse. Success on either path confirms that the interactions and extraction admission were committed. The response includes the request ID and storage routing. learning_status is "done" if this request is covered, otherwise "deferred". Creation counts come from completed windows covering this request's new interactions; they exclude later windows that only reuse its interactions as overlap context.

With wait_for_response=True, the server waits only for this request's required profile/playbook coverage, within a 240-second budget including admission. A timeout or full waiter budget returns the accepted request with learning_status="deferred"; extraction continues. An incomplete window can remain pending until more interactions arrive, an optional completion hint, or its configured session inactivity deadline (six hours by default). Automatic finalization retains the usefulness judge. An admission timeout returns HTTP 504 whose detail is an object carrying reason="admission_timeout" and the request_id; use that ID to reconcile the attempt through logs or support before retrying, because the commit may still complete after the deadline. A temporarily missing learning-status record does not prove that retrying is safe.

If the server cannot admit the publish at all, it returns HTTP 503 with a detail object carrying reason="capacity_deadline_exceeded" and the request_id. Unlike the 504, nothing was admitted and nothing will commit, so the publish is safe to retry. The request_id is a correlation handle for logs and support, not something to resend: publish_interaction has no request_id parameter, so a retry necessarily arrives under a new server-minted ID.

A request that exceeds the server's own backstop before the route can answer returns a generic HTTP 504 whose detail is the string "Request timeout", alongside reason="backstop_timeout" and a correlation_id. Treat that outcome as indeterminate. The backstop wraps the whole route invocation, not just request parsing, so the absent request_id does not establish that nothing happened — the publish may already have been admitted. Reconcile with the correlation_id before retrying rather than assuming a retry cannot duplicate.

Raises pydantic.ValidationError locally, before any HTTP request, when an interaction is contradictory or the whole batch is empty:

  • every interaction is empty (no content, shadow_content, expert_content, interacted_image_url, image_encoding, tools_used, citations, retrieved_learnings, and a user_action of none)
  • a user_action other than none with no user_action_description
  • both interacted_image_url and image_encoding set

The message names the offending interaction_data_list index. Because this fires client-side, try/except around HTTP status codes will not catch it, and there is no response object with success=False — the equivalent over raw HTTP is a 422. A missing or blank session_id raises ValueError.

Individually empty interactions are dropped rather than raising, so one empty placeholder turn does not fail a batch that also carries real turns. Unrecognised keys are discarded, not rejected — but both the drops and the unbound field names are listed in the returned warnings, so check it rather than only success. See InteractionData.

Evaluation-Only Publish

Set evaluation_only=True when a session should be graded but should not teach Reflexio new profiles or playbooks, such as an offline candidate transcript. Do not use it for a retrieval-experiment holdout: both experiment arms should publish and learn normally so retrieval is the only controlled difference.

from reflexio import InteractionData, ReflexioClient

client = ReflexioClient()

client.publish_interaction(
    user_id="user_123",
    session_id="session_001",
    source="prod_without_reflexio",
    agent_version="v2.1.0",
    evaluation_only=True,
    interactions=[
        InteractionData(role="User", content="Can you help me reset my account password?"),
        InteractionData(role="Agent", content="Open Account Settings, choose Security, then select Reset Password."),
    ],
)
curl -X POST "${REFLEXIO_URL:-https://www.reflexio.ai}/api/publish_interaction" \
  -H "User-Agent: my-agent-reflexio" \
  -H "Authorization: Bearer $REFLEXIO_API_KEY" \
  -H "Content-Type: application/json" \
  --data @- <<'JSON'
{
  "user_id": "user_123",
  "session_id": "session_001",
  "source": "prod_without_reflexio",
  "agent_version": "v2.1.0",
  "evaluation_only": true,
  "interaction_data_list": [
    {
      "role": "User",
      "content": "Can you help me reset my account password?"
    },
    {
      "role": "Agent",
      "content": "Open Account Settings, choose Security, then select Reset Password."
    }
  ]
}
JSON

evaluation_only=True stores the request, schedules session-level success evaluation if the session passes agent_success_config.evaluation_only_sampling_rate, and excludes the request from profile extraction, playbook extraction, and aggregation. When that override is null, it inherits the current agent_success_config.sampling_rate. Retrieved-learning evaluation keeps its independent retrieved_learning_sampling_rate. The flag does not bypass the session inactivity delay, and it is not an experiment grouping dimension. Keep evaluation_only consistent across every publish in one session.

Interaction Dict Fields

Prop

Type

Example 1: Basic Text Conversation

# Simple conversation (fire-and-forget)
client.publish_interaction(
    user_id="user_123",
    interactions=[
        {"role": "User", "content": "What's the weather like?"},
        {"role": "Agent", "content": "It's sunny and 72°F today."}
    ],
    source="support-agent:v2",
    session_id="session_001"
)
curl -X POST "${REFLEXIO_URL:-https://www.reflexio.ai}/api/publish_interaction" \
  -H "User-Agent: my-agent-reflexio" \
  -H "Authorization: Bearer $REFLEXIO_API_KEY" \
  -H "Content-Type: application/json" \
  --data @- <<'JSON'
{
  "user_id": "user_123",
  "interaction_data_list": [
    {
      "role": "User",
      "content": "What's the weather like?"
    },
    {
      "role": "Agent",
      "content": "It's sunny and 72°F today."
    }
  ],
  "source": "support-agent:v2",
  "session_id": "session_001"
}
JSON

Example 2: Publish Retrieved Learnings

Attach every Reflexio profile or playbook injected into the agent's context to the response that used it. This enables per-learning relevance and impact analysis when the session is evaluated.

# Report the Reflexio context used to produce the agent response.
client.publish_interaction(
    user_id="user_123",
    interactions=[
        {"role": "User", "content": "Recommend a laptop for my next work trip."},
        {
            "role": "Agent",
            "content": "Choose a lightweight model with long battery life.",
            "retrieved_learnings": [
                {"kind": "profile", "learning_id": "prof-abc123"},
                {"kind": "user_playbook", "learning_id": "42"},
                {"kind": "agent_playbook", "learning_id": "7"},
            ],
        },
    ],
    session_id="session_001",
    source="support-agent:v2",
)
curl -X POST "${REFLEXIO_URL:-https://www.reflexio.ai}/api/publish_interaction" \
  -H "User-Agent: my-agent-reflexio" \
  -H "Authorization: Bearer $REFLEXIO_API_KEY" \
  -H "Content-Type: application/json" \
  --data @- <<'JSON'
{
  "user_id": "user_123",
  "interaction_data_list": [
    {
      "role": "User",
      "content": "Recommend a laptop for my next work trip."
    },
    {
      "role": "Agent",
      "content": "Choose a lightweight model with long battery life.",
      "retrieved_learnings": [
        {"kind": "profile", "learning_id": "prof-abc123"},
        {"kind": "user_playbook", "learning_id": "42"},
        {"kind": "agent_playbook", "learning_id": "7"}
      ]
    }
  ],
  "session_id": "session_001",
  "source": "support-agent:v2"
}
JSON

Example 3: Multi-Turn Conversation with Agent Version

# Track which agent version handled the interaction
client.publish_interaction(
    user_id="customer_456",
    interactions=[
        {"role": "User", "content": "I need help choosing a laptop"},
        {"role": "Agent", "content": "I'd be happy to help! What's your budget?"},
        {"role": "User", "content": "Around $1000 for programming work"},
        {"role": "Agent", "content": "For programming, I recommend the ThinkPad X1..."}
    ],
    source="support-agent:v2",
    agent_version="v2.1.0",
    session_id="purchase_flow_789"
)
curl -X POST "${REFLEXIO_URL:-https://www.reflexio.ai}/api/publish_interaction" \
  -H "User-Agent: my-agent-reflexio" \
  -H "Authorization: Bearer $REFLEXIO_API_KEY" \
  -H "Content-Type: application/json" \
  --data @- <<'JSON'
{
  "user_id": "customer_456",
  "interaction_data_list": [
    {
      "role": "User",
      "content": "I need help choosing a laptop"
    },
    {
      "role": "Agent",
      "content": "I'd be happy to help! What's your budget?"
    },
    {
      "role": "User",
      "content": "Around $1000 for programming work"
    },
    {
      "role": "Agent",
      "content": "For programming, I recommend the ThinkPad X1..."
    }
  ],
  "source": "support-agent:v2",
  "agent_version": "v2.1.0",
  "session_id": "purchase_flow_789"
}
JSON

Example 4: Image Interactions

import base64

def encode_image(path):
    with open(path, "rb") as f:
        return base64.b64encode(f.read()).decode("utf-8")

# User shares an image
client.publish_interaction(
    user_id="user_123",
    interactions=[
        {
            "role": "User",
            "content": "What do you think of this outfit?",
            "image_encoding": encode_image("outfit.jpg")
        },
        {
            "role": "Agent",
            "content": "Great choice! The colors complement each other well."
        }
    ],
    source="styling_consultation",
    session_id="style_session_001"
)
curl -X POST "${REFLEXIO_URL:-https://www.reflexio.ai}/api/publish_interaction" \
  -H "User-Agent: my-agent-reflexio" \
  -H "Authorization: Bearer $REFLEXIO_API_KEY" \
  -H "Content-Type: application/json" \
  --data @- <<'JSON'
{
  "user_id": "user_123",
  "interaction_data_list": [
    {
      "role": "User",
      "content": "What do you think of this outfit?",
      "image_encoding": "<encode_image_result>"
    },
    {
      "role": "Agent",
      "content": "Great choice! The colors complement each other well."
    }
  ],
  "source": "styling_consultation",
  "session_id": "style_session_001"
}
JSON

Example 5: Expert Content for Learning from Experts

# Provide expert ideal responses for playbook extraction
client.publish_interaction(
    user_id="user_123",
    interactions=[
        {"role": "User", "content": "What is your return policy for electronics?"},
        {
            "role": "Agent",
            "content": "You can return electronics within 30 days.",
            "expert_content": (
                "Electronics can be returned within 30 days with the original receipt. "
                "Items must be in original packaging. Opened software is final sale. "
                "Defective items have a 90-day return window."
            )
        }
    ],
    source="expert_review",
    agent_version="v1.0",
    session_id="expert_session_001"
)
curl -X POST "${REFLEXIO_URL:-https://www.reflexio.ai}/api/publish_interaction" \
  -H "User-Agent: my-agent-reflexio" \
  -H "Authorization: Bearer $REFLEXIO_API_KEY" \
  -H "Content-Type: application/json" \
  --data @- <<'JSON'
{
  "user_id": "user_123",
  "interaction_data_list": [
    {
      "role": "User",
      "content": "What is your return policy for electronics?"
    },
    {
      "role": "Agent",
      "content": "You can return electronics within 30 days.",
      "expert_content": "Electronics can be returned within 30 days with the original receipt. Items must be in original packaging. Opened software is final sale. Defective items have a 90-day return window."
    }
  ],
  "source": "expert_review",
  "agent_version": "v1.0",
  "session_id": "expert_session_001"
}
JSON

Example 6: User Action Tracking

# Track user behavior on your platform
client.publish_interaction(
    user_id="shopper_789",
    interactions=[
        {
            "user_action": "click",
            "user_action_description": "Clicked 'Add to Cart' for MacBook Pro",
            "interacted_image_url": "https://store.com/products/macbook-pro.jpg"
        },
        {
            "user_action": "scroll",
            "user_action_description": "Scrolled through laptop accessories"
        }
    ],
    source="ecommerce",
    session_id="shopping_session_001"
)
curl -X POST "${REFLEXIO_URL:-https://www.reflexio.ai}/api/publish_interaction" \
  -H "User-Agent: my-agent-reflexio" \
  -H "Authorization: Bearer $REFLEXIO_API_KEY" \
  -H "Content-Type: application/json" \
  --data @- <<'JSON'
{
  "user_id": "shopper_789",
  "interaction_data_list": [
    {
      "user_action": "click",
      "user_action_description": "Clicked 'Add to Cart' for MacBook Pro",
      "interacted_image_url": "https://store.com/products/macbook-pro.jpg"
    },
    {
      "user_action": "scroll",
      "user_action_description": "Scrolled through laptop accessories"
    }
  ],
  "source": "ecommerce",
  "session_id": "shopping_session_001"
}
JSON

search_interactions

Search for user interactions using semantic queries.

response = client.search_interactions(
    user_id="user_123",
    query="laptop recommendations",
    top_k=5,
    most_recent_k=10
)
curl -X POST "${REFLEXIO_URL:-https://www.reflexio.ai}/api/search_interactions" \
  -H "User-Agent: my-agent-reflexio" \
  -H "Authorization: Bearer $REFLEXIO_API_KEY" \
  -H "Content-Type: application/json" \
  --data @- <<'JSON'
{
  "user_id": "user_123",
  "query": "laptop recommendations",
  "top_k": 5,
  "most_recent_k": 10
}
JSON

Prop

Type


get_interactions

Retrieve user interactions without semantic search.

response = client.get_interactions(
    user_id="user_123",
    top_k=50
)
curl -X POST "${REFLEXIO_URL:-https://www.reflexio.ai}/api/get_interactions" \
  -H "User-Agent: my-agent-reflexio" \
  -H "Authorization: Bearer $REFLEXIO_API_KEY" \
  -H "Content-Type: application/json" \
  --data @- <<'JSON'
{
  "user_id": "user_123",
  "top_k": 50
}
JSON

Prop

Type


get_all_interactions

Get all user interactions across all users (admin operation).

response = client.get_all_interactions(limit=100)
curl -X GET "${REFLEXIO_URL:-https://www.reflexio.ai}/api/get_all_interactions?limit=100" \
  -H "User-Agent: my-agent-reflexio" \
  -H "Authorization: Bearer $REFLEXIO_API_KEY"

Prop

Type

Returns: GetInteractionsViewResponse with interactions from all users.


get_learning_status

Poll the learning status for a previously published request. Call this after a deferred publish_interaction (where wait_for_response=False). The response from the deferred path carries learning_status="deferred" to signal that extraction has been queued; use this method to track progress.

status = client.get_learning_status(request_id="req_abc123")
# Returns one of: "pending", "processing", "done", "failed", "not_tracked"
curl -X GET "${REFLEXIO_URL:-https://www.reflexio.ai}/api/learning_status?request_id=req_abc123" \
  -H "User-Agent: my-agent-reflexio" \
  -H "Authorization: Bearer $REFLEXIO_API_KEY"

Prop

Type

Returns: str — one of "pending" | "processing" | "done" | "failed" | "not_tracked". The HTTP response additionally includes a reason.

When extraction overflow protection is enabled, failed with reason input_budget_exceeded means the window is held for explicit recovery. input_budget_configuration_error remains visible while configuration retries use exponential backoff, capped at five minutes; correcting configuration can resume the same window without resetting its consumed quota. The original interactions remain stored and the window has not completed. Use the existing request ID when contacting your operator or support. Transient provider failures continue to retry automatically.

Status values:

ValueMeaning
pendingWaiting for a window, capacity, or a transient retry
processingA worker holds a window needed to cover this request
doneAll required cursors cover this request, including valid empty or skipped results
failedExtraction is held or retrying configuration; read the HTTP reason
not_trackedPublished before durable stream admission was introduced

Transient extraction failures retry with backoff and retain the existing backup LLM provider behavior. done describes committed extraction coverage; billing delivery and derived post-processing can finish afterward. Previously extracted data remains available, and historical interactions are not automatically replayed.

The Python method returns only the status string. To obtain reason, use the authenticated HTTP request in the cURL tab above and read its JSON response.

Example: Publish and poll until done

Set REFLEXIO_URL to your server endpoint for self-hosted deployments. A configuration error continues polling within this bounded attempt; correction may take longer, so callers can poll again with the same request ID.

import os
import time

import requests

response = client.publish_interaction(
    user_id="user_123",
    interactions=[{"role": "User", "content": "Help me with X"}],
    session_id="sess_001",
    wait_for_response=False,
)
# response.learning_status == "deferred"

# The deferred response carries a request_id directly — no need to make a
# throwaway wait_for_response=True call first.
request_id = response.request_id

for _ in range(30):  # poll up to 30 seconds
    status = client.get_learning_status(request_id)
    if status == "failed":
        detail = requests.get(
            f"{os.getenv('REFLEXIO_URL', 'https://www.reflexio.ai').rstrip('/')}/api/learning_status",
            params={"request_id": request_id},
            headers={
                "Authorization": f"Bearer {os.environ['REFLEXIO_API_KEY']}",
                "User-Agent": "my-agent-reflexio",
            },
            timeout=10,
        )
        detail.raise_for_status()
        reason = detail.json().get("reason")
        if reason != "input_budget_configuration_error":
            # input_budget_exceeded needs explicit operator recovery.
            raise RuntimeError(f"Learning held for {request_id}: {reason}")
        # Configuration retries may resume after the operator fixes configuration.
    if status in ("done", "not_tracked"):
        print(f"Learning {status}")
        break
    time.sleep(1)

delete_interaction

Delete a specific user interaction.

response = client.delete_interaction(
    user_id,
    interaction_id,
    wait_for_response=False
)
curl -X DELETE "${REFLEXIO_URL:-https://www.reflexio.ai}/api/delete_interaction" \
  -H "User-Agent: my-agent-reflexio" \
  -H "Authorization: Bearer $REFLEXIO_API_KEY" \
  -H "Content-Type: application/json" \
  --data @- <<'JSON'
{
  "user_id": "<user_id>",
  "interaction_id": "<interaction_id>"
}
JSON

Prop

Type

finalize_session / finalize_session_async

Optionally indicate that a session's saved prefix is ready for evaluation. This is not required for direct publishers: the backend also evaluates incomplete windows after six hours without new publications in the same session by default.

result = client.finalize_session(user_id="user123", session_id="session-1")
# Native async:
result = await client.finalize_session_async(user_id="user123", session_id="session-1")
# Bound session:
result = client.for_session("session-1").finalize_session(user_id="user123")

HTTP: POST /api/finalize_session, JSON {"user_id":"user123","session_id":"session-1"}. Both fields are required nonempty strings. Authentication determines organization and project. FinalizeSessionResponse contains success, accepted (whether a saved boundary exists), and message. Success is acceptance, not completed extraction. Unknown sessions are successful no-ops; duplicate hints do not reopen covered input. Later messages in the same session are accepted.

Alternatively pass session_finished=True to either publish method (default False); the final messages and completion marker commit together. The judge still decides whether the short input warrants extraction. A negative decision is recorded as examined. Explicit manual force_extraction retains its bypass behavior.

Config.extraction_idle_timeout_seconds defaults to 21600; zero disables idle fallback for new publications. Policy changes apply to subsequent publications. The delay is an operator policy, not proof a session ended. Legacy admissions without server timing require explicit completion or normal window eligibility; they are not automatically backfilled. Embedded use requires a live process and reachable library handle, and reopening resumes durable work.