Create Context Requests
Open a context request when the answer does not exist yet.
This is the handoff path for genuinely missing knowledge. Your app stays in
charge of the workflow, but Valmar handles routing the question to the right
people. In the Admin UI these appear as Context Requests; the SDK
calls them context_requests in Python and contextRequests in TypeScript.
What to include
- A concrete question the assigned person can answer directly.
- Enough background context for the human to understand the situation.
- The narrowest workflow scope you can start with.
Basic create-and-poll
Start with polling while you prove the request loop end to end.
handle = client.context_requests.create(
"How do we handle database migrations in production?",
background_context="Planning a schema change for the orders table",
)
print(f"Request created: {handle.context_request_id}")
print(f"Status: {handle.status}")
request = client.context_requests.get(handle.context_request_id)
if request.status == "completed":
print(request.result_summary)const handle = await valmar.contextRequests.create({
question: "How do we handle database migrations in production?",
backgroundContext: "Planning a schema change for the orders table",
});
console.log(`Request created: ${handle.contextRequestId}`);
console.log(`Status: ${handle.status}`);
const request = await valmar.contextRequests.get(handle.contextRequestId);
if (request.status === "completed") {
console.log(request.resultSummary);
}With optional parameters
Give the assigned person more to work with: what your app already tried, which agent inside your product is asking, and how the request should be attributed.
handle = client.context_requests.create(
question="What's the policy on issuing refunds above $500?",
background_context=(
"Customer #4821 is requesting a $750 refund for a duplicate charge. "
"The duplicate charge is confirmed in Stripe."
),
already_tried=(
"Searched the knowledge base for 'refund policy' and 'high value refund'; "
"only found the standard <$200 policy."
),
requesting_application="support-copilot",
hidden_metadata={"external_case_id": "CASE-4821"},
source_agent_config_id="77777777-7777-4777-8777-777777777777",
)
# Status values include pending, waiting_for_reply, waiting_for_review, and completed
print(f"Status: {handle.status}")const handle = await valmar.contextRequests.create({
question: "What's the policy on issuing refunds above $500?",
backgroundContext:
"Customer #4821 is requesting a $750 refund for a duplicate charge. " +
"The duplicate charge is confirmed in Stripe.",
alreadyTried:
"Searched the knowledge base for 'refund policy' and 'high value refund'; " +
"only found the standard <$200 policy.",
requestingApplication: "support-copilot",
hiddenMetadata: { external_case_id: "CASE-4821" },
sourceAgentConfigId: "77777777-7777-4777-8777-777777777777",
});
// Status values include pending, waiting_for_reply, waiting_for_review, and completed
console.log(`Status: ${handle.status}`);| Parameter | What it does |
|---|---|
background_context / backgroundContext | Free-form context the human reviewer needs to make sense of the question. |
already_tried / alreadyTried | What your system has already attempted. Stops the assignee from suggesting the same things. |
requesting_application / requestingApplication | A label for which of your apps or agents asked. Surfaces in the Admin UI. |
hidden_metadata / hiddenMetadata | An opaque string-to-string map for matching the request to records in your systems. REST uses hidden_metadata. |
source_agent_config_id / sourceAgentConfigId | Link the request to a specific configured agent for analytics and routing. |
Hidden metadata stays outside Valmar AI
hidden_metadata is fixed when the request is created and copied unchanged to
every unstructured context resource saved from that request. Request detail,
list, context read, trace, and search responses return it so your application
can match records client-side. Keys and values must be strings. The field is
not indexed, filterable, available through MCP, or shown to Valmar AI agents.
Status values
A context request normally moves through pending → waiting_for_reply →
completed. It may pause in unassigned, deferred, or
waiting_for_review. Terminal alternatives are filtered_out, deleted,
timed_out, and failed. A not_resolved answer always enters
waiting_for_review instead of creating saved knowledge.
Use Valmar as a tool in your agent
Creating context requests is well-suited to be exposed as an agent tool: the model decides when it has exhausted its options and hands off to a real person. There are two ways to do this.
As an MCP server
If your agent runtime already speaks MCP, point it at the Valmar MCP endpoint. See MCP Integration. Nothing else to write.
As a custom tool (LangChain example)
When you want explicit control over how the model sees the tool — its
description, schema, return formatting — wrap the SDK call yourself. The
example below uses LangChain's @tool decorator
and create_agent, but the same shape works for any agent library
(LlamaIndex FunctionTool, OpenAI's function-calling API, the Anthropic
SDK's tools=[...] parameter, CrewAI tools, etc.) — wrap
client.context_requests.create in whatever the library calls a tool.
uv add langchain langchain-openaiimport os
from langchain.agents import create_agent
from langchain.tools import tool
from valmar import Valmar
valmar = Valmar(
api_key=os.environ["VALMAR_API_KEY"],
organization_id=os.environ["VALMAR_ORGANIZATION_ID"],
project_id=os.environ["VALMAR_PROJECT_ID"],
base_url=os.environ["VALMAR_BASE_URL"],
)
@tool
def request_context(
question: str,
background_context: str,
already_tried: str = "",
) -> str:
"""Hand off a question to a real person at the company.
Use this ONLY after `search_company_context` returns no useful
result. Be specific in the question and include enough background
that a human can answer without further clarification.
Args:
question: A concrete question the assignee can answer directly.
background_context: What you already know about the situation.
already_tried: What you've already attempted (so the assignee doesn't repeat it).
"""
handle = valmar.context_requests.create(
question=question,
background_context=background_context,
already_tried=already_tried or None,
requesting_application="my-agent",
)
return (
f"Context request created (id={handle.context_request_id}, "
f"status={handle.status}). A teammate will respond; you can stop here."
)
agent = create_agent(
"openai:gpt-5.4",
tools=[request_context],
)The full loop
For the canonical pattern, give the agent both
search_company_context and
request_context as tools. The model will search first and only
escalate when it has to.