# Run Research

# 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](../authentication.md) and [scopes](../api-reference/scopes.md).
- 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](../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

- [agents.md](../agents.md) for agent lifecycle and SSE event details.
- [api-reference/tools/workflows.md](../api-reference/tools/workflows.md) for `agents.*` schemas.
- [api-reference/tools/data.md](../api-reference/tools/data.md) for the `data.*` tools the researcher calls internally.
- [api-reference/tools/assets.md](../api-reference/tools/assets.md) for fetching and versioning the report.
- [recipes/draft-blog-post.md](./draft-blog-post.md) for a content-creation companion flow.
