Docs

Run a Research Investigation

End-to-end recipe for producing a research_report artifact using the researcher agent profile. The agent decomposes the topic into sub-questions, queries your business substrate and knowledge base, synthesizes findings with citations, and saves a single artifact with a structured report body.

Prerequisites

  • An API key with scopes workflows:write (start the agent) and assets:read (fetch the resulting report). See authentication.md and scopes.
  • A business with data loaded. The researcher leans heavily on data.* and knowledge_base.search; a cold tenant produces a shallow report.

Export for the examples:

bash
export API_KEY="sk_live_your_key_here"

Steps

1. Start the agent

Unlike content_writer, the researcher does not pause for approvals. You get a terminal status on the first run.

bash
curl -sS -X POST "https://app.amdahl.co/api/platform/v1/agents/run" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "profile_id": "researcher",
    "task": "What drives churn among our mid-market customers in the last 90 days?",
    "input_params": {
      "audience": "internal-revops",
      "depth": "deep",
      "must_cover": ["top 3 root causes", "segment cuts", "leading indicators"]
    },
    "async": true
  }'

Expected response:

json
{
  "success": true,
  "session_id": "as_01J0...",
  "status": "queued",
  "profile_id": "researcher",
  "created_at": "2026-04-14T19:02:44.001Z",
  "async": true
}

What this tells you: the investigation is enqueued. Keep the session_id.

2. (Optional) Subscribe to the live SSE stream

If you want UI-grade progress updates instead of poll-based status, open the Server-Sent Events stream. Each event is a JSON payload pushed as the agent thinks and calls tools. Useful for showing a progress panel.

bash
curl -sN "https://app.amdahl.co/api/platform/v1/agents/$SESSION_ID/stream" \
  -H "Authorization: Bearer $API_KEY"

TypeScript client using the fetch streaming API:

ts
async function streamAgent(sessionId: string) {
  const res = await fetch(`${process.env.AMDAHL_URL}/api/platform/v1/agents/${sessionId}/stream`, {
    headers: { Authorization: `Bearer ${process.env.API_KEY}` },
  })
  if (!res.ok || !res.body) throw new Error(`stream failed: ${res.status}`)

  const reader = res.body.getReader()
  const decoder = new TextDecoder()
  let buffer = ''

  for (;;) {
    const { value, done } = await reader.read()
    if (done) break
    buffer += decoder.decode(value, { stream: true })
    const frames = buffer.split('\n\n')
    buffer = frames.pop() ?? ''
    for (const frame of frames) {
      const line = frame.split('\n').find(l => l.startsWith('data: '))
      if (!line) continue
      const event = JSON.parse(line.slice(6))
      console.log(event.type, event.data)
      if (event.type === 'session.completed' || event.type === 'session.failed') {
        reader.cancel()
        return event
      }
    }
  }
}

Event types you will see: tool.started, tool.completed, turn.completed, session.completed, session.failed. Payloads follow the shape documented in api-reference/tools/workflows.md.

3. Or poll the status endpoint

bash
curl -sS "https://app.amdahl.co/api/platform/v1/agents/$SESSION_ID/status" \
  -H "Authorization: Bearer $API_KEY"

Mid-run response:

json
{
  "session_id": "as_01J0...",
  "status": "running",
  "turn_count": 9,
  "current_tool": "data.cluster_search",
  "updated_at": "2026-04-14T19:03:57.445Z"
}

What this tells you: the agent is mid-investigation. The researcher profile has a maxTurns cap of 20. If turn_count reaches 20 without completion, the run ends with status=failed and error.code='max_turns_exceeded'.

4. Wait for completion

Terminal states: complete, failed, canceled. A successful run:

json
{
  "session_id": "as_01J0...",
  "status": "complete",
  "result_summary": "6 sub-questions answered across 14 citations. Primary root cause: onboarding drop-off at week 2.",
  "result_artifact_id": "art_01J0...",
  "turn_count": 17,
  "updated_at": "2026-04-14T19:07:12.998Z"
}

5. Fetch the research report

bash
curl -sS "https://app.amdahl.co/api/platform/v1/artifacts/$ARTIFACT_ID" \
  -H "Authorization: Bearer $API_KEY"

Expected response (shortened):

json
{
  "id": "art_01J0...",
  "artifact_type": "research_report",
  "status": "draft",
  "title": "Mid-market churn drivers, last 90 days",
  "content_markdown": "# Mid-market churn drivers...\n\n## Summary\n...\n",
  "content_json": {
    "summary": "Churn concentrated in the week-2 onboarding gap...",
    "sub_questions": [
      {
        "question": "What are the top 3 root causes?",
        "answer": "...",
        "confidence": "high",
        "citations": ["c_01", "c_05", "c_07"]
      }
    ],
    "key_findings": [{ "point": "Week-2 activation drop of 31%.", "citations": ["c_01"] }],
    "gaps": ["No qualitative signal from churned SMB customers; survey response rate < 5%."],
    "citations": [
      {
        "id": "c_01",
        "source_type": "data_query",
        "title": "Cohort activation, last 90d",
        "snippet": "..."
      }
    ]
  },
  "version": 1,
  "created_at": "2026-04-14T19:07:12.810Z"
}

What this tells you: content_json is the structured report. content_markdown is a rendered version for humans. citations ties every finding back to a specific data query or document.

Common issues

  • status=failed with error.code='max_turns_exceeded': scope was too broad. Split into narrower tasks or set input_params.depth='shallow'.
  • Empty gaps and shallow findings: your substrate has no matching evidence. Add data via data.* ingestion or upload documents to the knowledge base first.
  • Stream closes early with no session.completed event: network timeout at the proxy layer. Fall back to polling GET /agents/:id/status.
  • result_artifact_id is null on status=complete: the agent finished without calling artifacts.create. This is a profile bug; inspect result_summary for reasoning and file an issue. Retry the task.
  • Citations reference ids that are not in content_json.citations: the profile caught a synthesis bug. Treat the finding as unsupported and re-run with a narrower task.

See also