DeepResearch tasks support optional human-in-the-loop checkpoints that pause execution at key decision points, allowing users to review and guide the research process.
HITL is only available for individual deep research tasks. It is not available for batch requests .
Available checkpoints
Enable any combination of four checkpoints that fire in order during the research lifecycle:
Checkpoint Phase When it fires planning_questionsPre-research Before research begins — the agent asks clarifying questions plan_reviewPre-research After planning — user reviews the research plan source_reviewPost-research After research — user filters sources by domain outline_reviewPost-research After source filtering — user reviews the report outline
Quick start
from valyu import Valyu
client = Valyu()
# Create a task with HITL checkpoints
task = client.deepresearch.create(
query = "Analyze the competitive landscape of AI chip manufacturers" ,
mode = "heavy" ,
hitl = {
"planning_questions" : True ,
"plan_review" : True ,
"source_review" : True ,
"outline_review" : True ,
},
)
task_id = task.deepresearch_id
# Poll until a checkpoint fires or task completes
import time
while True :
status = client.deepresearch.status(task_id)
if status.status == "awaiting_input" :
interaction = status.interaction
print ( f "Checkpoint: { interaction.type } " )
print ( f "Data: { interaction.data } " )
# Build your response based on the checkpoint type
if interaction.type == "planning_questions" :
response = {
"answers" : [
{ "question" : q[ "question" ], "answer" : "Focus on NVIDIA, AMD, and Intel" }
for q in interaction.data[ "questions" ]
]
}
elif interaction.type in ( "plan_review" , "outline_review" ):
response = { "approved" : True }
elif interaction.type == "source_review" :
response = {
"included_domains" : [ "sec.gov" , "plos.org" ],
"excluded_domains" : [],
}
# Respond to the checkpoint
client.deepresearch.respond(
task_id,
interaction_id = interaction.interaction_id,
response = response,
)
elif status.status in ( "completed" , "failed" , "cancelled" ):
break
time.sleep( 5 )
print (status.output)
Task lifecycle with HITL
When a checkpoint fires, the task transitions through these statuses:
running → awaiting_input → (user responds) → running → ... → completed
If the user doesn’t respond within 5 minutes, the task transitions to paused:
running → awaiting_input → (5 min timeout) → paused → (user responds later) → queued → running
Status values
Status Meaning awaiting_inputCheckpoint active, container holding capacity, fast resume on response pausedCheckpoint timed out, state saved, respond anytime to resume runningResearch or writing in progress queuedRe-enqueued after responding to a paused task
Responding to a paused task still works — the task re-enqueues at highest priority. The only difference is a brief cold-start delay as the container restarts.
Checkpoint response shapes
Each checkpoint type expects a specific response format.
Planning questions
The agent asks clarifying questions before starting research.
Interaction data:
{
"questions" : [
{
"question" : "What geographic regions should the research focus on?" ,
"context" : "The query mentions global markets — narrowing scope improves depth"
},
{
"question" : "Are there specific competitors you want analyzed?"
}
]
}
Response:
{
"answers" : [
{ "question" : "What geographic regions?" , "answer" : "North America and EU" },
{ "question" : "Specific competitors?" , "answer" : "Tesla, BYD, Rivian" }
]
}
Field Type Required answersArray<{ question, answer }>Yes answers[].questionstringYes answers[].answerstringYes
Plan review
Review the research plan before execution.
Interaction data:
{
"plan" : "I'll research this topic by first examining..." ,
"estimated_steps" : 15 ,
"research_areas" : [ "Market size analysis" , "Competitive landscape" , "Regulatory environment" ]
}
Response (approve):
Response (request modifications):
{
"approved" : false ,
"modifications" : "Focus more on battery supply chains and less on historical context"
}
Field Type Required approvedbooleanYes modificationsstringNo — free-text guidance for the model
Source review
Filter sources by domain after the research phase.
Interaction data:
{
"domains" : [
{
"domain" : "sec.gov" ,
"source_count" : 8 ,
"avg_relevance_score" : 0.87 ,
"sources" : [
{ "source_id" : 1 , "title" : "SEC Filing: Tesla Annual Report" , "url" : "https://sec.gov/..." , "relevance_score" : 0.92 }
],
"ai_recommendation" : "include"
},
{
"domain" : "example.com" ,
"source_count" : 2 ,
"avg_relevance_score" : 0.31 ,
"sources" : [
{ "source_id" : 14 , "title" : "..." , "url" : "https://example.com/..." , "relevance_score" : 0.31 }
],
"ai_recommendation" : "exclude"
}
],
"total_sources" : 42
}
Response:
{
"included_domains" : [ "sec.gov" , "plos.org" ],
"excluded_domains" : [ "example.com" ]
}
Field Type Required included_domainsstring[]Yes (can be empty [] to accept AI recommendations) excluded_domainsstring[]Yes (can be empty [])
Domains not listed in either array fall back to the AI recommendation.
Outline review
Review the report outline before writing begins.
Interaction data:
{
"outline" : "1. Introduction \n 2. Market Analysis \n 3. Competitive Landscape \n 4. Conclusion" ,
"sections" : [
{ "title" : "Introduction" , "description" : "Overview of the research topic" , "estimated_length" : "short" },
{ "title" : "Market Analysis" , "description" : "Market size and growth analysis" , "estimated_length" : "long" },
{ "title" : "Competitive Landscape" , "description" : "Key players and positioning" , "estimated_length" : "long" },
{ "title" : "Conclusion" , "description" : "Summary and implications" , "estimated_length" : "short" }
]
}
Response (approve):
Response (request modifications):
{
"approved" : false ,
"modifications" : "Add a section on regulatory risks between Market Analysis and Competitive Landscape"
}
Field Type Required approvedbooleanYes modificationsstringNo — free-text guidance for the model
Respond endpoint
POST /v1/deepresearch/tasks/:id/respond
Headers:
Header Value x-api-keyYour API key Content-Typeapplication/json
Body:
{
"interaction_id" : "string (must match task.interaction.interaction_id)" ,
"response" : { }
}
Response codes
Code Meaning Example 200 Accepted (hot path) { "success": true, "status": "running" }200 Accepted (cold path) { "success": true, "status": "queued" }400 Validation error { "error": "response.answers must be an array" }409 Wrong status or ID mismatch { "error": "Cannot respond to task with status: running" }
Interaction fields on task status
When HITL is enabled, these additional fields appear on the task status response:
Field Type Present when hitl_configobjectAlways (mirrors the request hitl param) interactionobjectStatus is awaiting_input or paused hitl_historyarrayAfter any checkpoint completes
Interaction history entry
Each entry in hitl_history contains:
Field Type Description interaction_idstringUnique checkpoint ID typestringCheckpoint type created_atintegerWhen the checkpoint fired (ms) responded_atintegerWhen the user responded (ms) — absent if timed out auto_continuedbooleantrue if timed out, false if user respondedresponseobjectThe user’s response — absent if timed out
Polling pattern
Standard HITL polling flow:
1. POST /v1/deepresearch/tasks → { deepresearch_id }
2. GET .../status → status: "running"
3. GET .../status → status: "awaiting_input", interaction: { ... }
4. POST .../respond → { status: "running" }
5. GET .../status → status: "running"
... (repeats for each enabled checkpoint)
6. GET .../status → status: "completed"
If a checkpoint times out:
3. GET .../status → status: "awaiting_input"
... (5 minutes pass)
4. GET .../status → status: "paused"
... (user responds later)
5. POST .../respond → { status: "queued" }
6. GET .../status → status: "running"
Best practices
Poll frequently during checkpoints Use a 2-3 second poll interval when expecting HITL checkpoints. Switch to standard intervals (5-10s) during research phases.
Handle both statuses Always check for both awaiting_input and paused — the user experience is identical, only resume speed differs.
Enable selectively Only enable checkpoints that add value for your use case. Each checkpoint adds latency equal to the user’s response time.
Use with heavy or max modes HITL works best with heavy or max modes where the research is substantial enough to benefit from human guidance.