Appearance
Request Lineage & Tracing
Track the full chain of execution across voice agents, text agents, and MCP tool calls. This page explains how UAPI's automatic lineage system works and how to query correlated logs.
Session Memory ≠ Session Lineage
This page covers lineage — cross-service correlation for observability and audit trails. For conversation memory (multi-turn chat history), see Session Management. These are two different systems that share the same conversationId key but serve different purposes.
Two Systems, Same conversationId
| System | Purpose | Mechanism | Scope |
|---|---|---|---|
Session Memory (session_manager) | Persist LLM chat history across turns | S3SessionManager loads/saves message arrays | Single agent, single conversation |
Session Lineage (X-UAPI-Session-Context) | Cross-service correlation & audit trail | HTTP header auto-injected on outbound calls | Cross-agent, cross-MCP, full chain |
Both use the same conversationId UUID — because the conversation IS the unit of identity. But they solve different problems:
- Session Memory answers: "What did this agent say 3 messages ago?"
- Session Lineage answers: "Which voice call triggered this MCP tool invocation?"
The Full Chain
📞 Phone Call arrives
→ Voice Agent (bidi runtime, conversationId: "voice-abc")
→ Text Agent (delegated, conversationId: "text-xyz")
→ MCP Tool Call (logged with parentConversationId: "text-xyz")At each hop, the platform automatically propagates lineage metadata. No manual configuration needed.
Where Lineage Is Stored
| Hop | Storage | Key Fields |
|---|---|---|
| Voice → Text Agent | AgentConversationTable | parentConversationId, parentAgentId |
| Agent → MCP Tool | RequestTable (log record) | parentConversationId, parentAgentId, parentRequestId |
| Agent → MCP (live) | userContext.sessionContext | conversationId, agentId, userId, parentConversationId, parentAgentId |
How It Works (Automatic)
Step 1: Voice Agent Starts a Session
When a voice call connects, the bidi runtime creates a session and installs context:
conversationId: "voice-abc"
agentId: "voice-agent-123"
userId: "user-456"
sessionType: "bidi"Step 2: Voice Agent Delegates to Text Agent
The voice agent calls a text agent via call_uapi_agent. The platform auto-injects an X-UAPI-Session-Context header:
X-UAPI-Session-Context: {"conversationId":"voice-abc","agentId":"voice-agent-123","userId":"user-456"}The text agent runtime parses this inbound header and:
- Stores
parentConversationId: "voice-abc"andparentAgentId: "voice-agent-123"on the text conversation record - Installs its OWN session context for downstream calls (with parent fields propagated)
Step 3: Text Agent Calls MCP Tools
When the text agent calls an MCP tool, its session context (including parent fields) is:
- Injected into the
X-UAPI-Session-Contextheader → available live asuserContext.sessionContext - Persisted to the RequestTable log record as
parentConversationId,parentAgentId,parentRequestId
Querying the Full Chain
Given a voice session → find all delegated text conversations
bash
# Find text agent conversations spawned by a voice call
curl -s "https://api.universalapi.co/logs?resourceId={textAgentId}&timeRange=24" \
-H "Authorization: Bearer YOUR_TOKEN" | jq '.data.logs[] | select(.parentConversationId == "voice-abc")'Or via DynamoDB directly:
python
# Scan AgentConversationTable for child conversations
response = table.scan(
FilterExpression='parentConversationId = :vid',
ExpressionAttributeValues={':vid': voice_session_id}
)Given a text agent session → find all MCP tool invocations
bash
# Get all MCP tool calls triggered by a specific agent session
curl -s "https://api.universalapi.co/logs?parentConversationId={textConversationId}" \
-H "Authorization: Bearer YOUR_TOKEN" | jqReturns full MCP log records including request.body (JSON-RPC payload), response.body, billing, and durationMs.
Given a voice session → find ALL MCP calls (two-hop)
Currently requires two queries:
python
# Step 1: Find text conversations spawned by the voice session
text_convs = table.scan(
FilterExpression='parentConversationId = :vid',
ExpressionAttributeValues={':vid': voice_session_id}
)
# Step 2: For each text conversation, query MCP logs
for conv in text_convs['Items']:
mcp_logs = requests.get(
f"https://api.universalapi.co/logs?parentConversationId={conv['conversationId']}",
headers={"Authorization": f"Bearer {token}"}
).json()Immediate Parent Only
The parentConversationId on MCP logs points to the immediate caller (the text agent), not the root voice session. To trace back to the original voice call, follow the chain: MCP log → text conversation → check that conversation's parentConversationId.
Field Reference
On AgentConversationTable Records
| Field | Type | Set When | Description |
|---|---|---|---|
conversationId | String | Always | This conversation's unique ID |
agentId | String | Always | The agent that owns this conversation |
parentConversationId | String | Delegated calls only | The parent agent's conversation ID |
parentAgentId | String | Delegated calls only | The parent agent's UUID |
On RequestTable (MCP Log Records)
| Field | Type | Set When | Description |
|---|---|---|---|
parentConversationId | String | Agent-triggered MCP calls | The calling agent's conversationId |
parentAgentId | String | Agent-triggered MCP calls | The calling agent's UUID |
parentRequestId | String | Agent-triggered MCP calls | The agent's request ID |
On userContext.sessionContext (Live in MCP Server Code)
| Field | Type | Description |
|---|---|---|
conversationId | String | The immediate caller's conversation ID |
agentId | String | The immediate caller's agent UUID |
userId | String | The end-user's UAPI user ID |
parentConversationId | String? | Parent agent's conversation (if delegated) |
parentAgentId | String? | Parent agent's UUID (if delegated) |
channelId | String? | Channel UUID (if via Slack, SMS, etc.) |
platform | String? | Platform name ("twilio-voice", "slack", etc.) |
What Lineage Does NOT Do
- ❌ Does not persist chat history — that's
session_manager/ S3SessionManager - ❌ Does not propagate through external HTTP calls — only UAPI domains
- ❌ Does not store the root ancestor — only the immediate parent (follow the chain for multi-hop)
- ❌ Does not require any configuration — fully automatic for all agent→agent and agent→MCP calls
Diagram: Complete Data Flow
┌─────────────────────────────────────────────────────────────────┐
│ LINEAGE SYSTEM │
│ │
│ ┌─────────────────────┐ X-UAPI-Session-Context header │
│ │ Voice Agent (bidi) │────────────────────────────────┐ │
│ │ conv: "voice-abc" │ │ │
│ └─────────────────────┘ ▼ │
│ ┌──────────────────┐│
│ │ Text Agent ││
│ │ conv: "text-xyz" ││
│ │ parent: "voice-" ││
│ └────────┬─────────┘│
│ │ │
│ X-UAPI-Session-Context │
│ │ │
│ ▼ │
│ ┌──────────────────┐│
│ │ MCP Server ││
│ │ (live access + ││
│ │ log persisted) ││
│ └──────────────────┘│
├─────────────────────────────────────────────────────────────────┤
│ SESSION MEMORY SYSTEM │
│ │
│ ┌─────────────────────┐ │
│ │ S3SessionManager │ ← Separate system, same conversationId│
│ │ Stores: messages[] │ ← Only for multi-turn memory │
│ │ Per: agent+conv │ ← No cross-agent propagation │
│ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘Related
- Session Management — Multi-turn conversation memory (
session_manager) - Session Context in MCP — Live access to lineage data in MCP servers
- Voice-to-Text Delegation — Voice agent → text agent patterns
- Logs API — Querying log records with lineage fields