> ## Documentation Index
> Fetch the complete documentation index at: https://docs.chatgrid.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Automate a Research Pipeline

> Batch-create source nodes, vectorize extracted text, and let AI synthesize findings with linked citations. 12 minutes.

## What you'll build

A structured research pipeline that creates multiple source nodes, vectorizes extracted source text, and produces an AI-synthesized report with edges linking conclusions back to source material.

* Batch-create 5+ source nodes in one API call
* Vectorize extracted source text for semantic search
* AI-generated synthesis across all sources
* Edges connecting findings to their source nodes

## Prerequisites

* ChatGrid API key ([get one here](/authentication))
* Node.js 18+ or Python 3.10+

## Step 1: Create a research board

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.chatgrid.ai/v1/boards \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"name": "AI Agent Market Research - Q1 2026"}'
  ```

  ```typescript Node.js theme={null}
  const board = await fetch("https://api.chatgrid.ai/v1/boards", {
    method: "POST",
    headers: { Authorization: `Bearer ${API_KEY}`, "Content-Type": "application/json" },
    body: JSON.stringify({ name: "AI Agent Market Research - Q1 2026" }),
  }).then((r) => r.json());
  const boardId = board.data.id;
  ```

  ```python Python theme={null}
  import requests
  board = requests.post("https://api.chatgrid.ai/v1/boards",
      headers={"Authorization": f"Bearer {API_KEY}"},
      json={"name": "AI Agent Market Research - Q1 2026"}).json()
  board_id = board["data"]["id"]
  ```
</CodeGroup>

## Step 2: Batch-create source nodes

Use the batch endpoint to add multiple source nodes in one call -- faster and atomic.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.chatgrid.ai/v1/boards/{boardId}/nodes/batch \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "operations": [
        {"action": "create", "data": {"type": "url", "position": {"x": 0, "y": 0}, "data": {"url": "https://a16z.com/ai-agents-landscape-2026"}}},
        {"action": "create", "data": {"type": "url", "position": {"x": 420, "y": 0}, "data": {"url": "https://sequoia.com/article/agentic-ai-market-map"}}},
        {"action": "create", "data": {"type": "url", "position": {"x": 840, "y": 0}, "data": {"url": "https://techcrunch.com/2026/02/15/enterprise-ai-agents-funding"}}},
        {"action": "create", "data": {"type": "youtube", "position": {"x": 1260, "y": 0}, "data": {"url": "https://youtube.com/watch?v=agent-market-deep-dive"}}},
        {"action": "create", "data": {"type": "url", "position": {"x": 1680, "y": 0}, "data": {"url": "https://mckinsey.com/capabilities/ai/ai-agents-enterprise-adoption"}}}
      ]
    }'
  ```

  ```typescript Node.js theme={null}
  const sources = [
    { type: "url", position: { x: 0, y: 0 }, data: { url: "https://a16z.com/ai-agents-landscape-2026" } },
    { type: "url", position: { x: 420, y: 0 }, data: { url: "https://sequoia.com/article/agentic-ai-market-map" } },
    { type: "url", position: { x: 840, y: 0 }, data: { url: "https://techcrunch.com/2026/02/15/enterprise-ai-agents-funding" } },
    { type: "youtube", position: { x: 1260, y: 0 }, data: { url: "https://youtube.com/watch?v=agent-market-deep-dive" } },
    { type: "url", position: { x: 1680, y: 0 }, data: { url: "https://mckinsey.com/capabilities/ai/ai-agents-enterprise-adoption" } },
  ];
  const created = await fetch(`https://api.chatgrid.ai/v1/boards/${boardId}/nodes/batch`, {
    method: "POST",
    headers: { Authorization: `Bearer ${API_KEY}`, "Content-Type": "application/json" },
    body: JSON.stringify({
      operations: sources.map((source) => ({ action: "create", data: source })),
    }),
  }).then((r) => r.json());
  ```

  ```python Python theme={null}
  sources = [
      {"type": "url", "position": {"x": 0, "y": 0}, "data": {"url": "https://a16z.com/ai-agents-landscape-2026"}},
      {"type": "url", "position": {"x": 420, "y": 0}, "data": {"url": "https://sequoia.com/article/agentic-ai-market-map"}},
      {"type": "url", "position": {"x": 840, "y": 0}, "data": {"url": "https://techcrunch.com/2026/02/15/enterprise-ai-agents-funding"}},
      {"type": "youtube", "position": {"x": 1260, "y": 0}, "data": {"url": "https://youtube.com/watch?v=agent-market-deep-dive"}},
      {"type": "url", "position": {"x": 1680, "y": 0}, "data": {"url": "https://mckinsey.com/capabilities/ai/ai-agents-enterprise-adoption"}},
  ]
  created = requests.post(f"https://api.chatgrid.ai/v1/boards/{board_id}/nodes/batch",
      headers={"Authorization": f"Bearer {API_KEY}"},
      json={"operations": [{"action": "create", "data": source} for source in sources]}).json()
  ```
</CodeGroup>

Save the node IDs from the individual node creation responses if you need to connect findings back to sources later.

## Step 3: Vectorize all sources

Vectorize extracted source text so AI can search across it semantically. Run calls in parallel for speed.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.chatgrid.ai/v1/boards/{boardId}/documents/vectorize \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "content": "AI agent market notes: enterprise buyers prioritize reliability, governance, and measurable workflow automation over generic copilots.",
      "metadata": {"source": "research-notes", "topic": "agentic-ai-market"}
    }'
  ```

  ```typescript Node.js theme={null}
  const researchNotes = [
    {
      content: "AI agent market notes: enterprise buyers prioritize reliability, governance, and measurable workflow automation over generic copilots.",
      metadata: { source: "research-notes", topic: "agentic-ai-market" },
    },
    {
      content: "Adoption trend: teams start with internal automation, then expand to customer-facing workflows once observability and approvals are in place.",
      metadata: { source: "analyst-summary", topic: "enterprise-adoption" },
    },
  ];
  await Promise.all(researchNotes.map((note) =>
    fetch(`https://api.chatgrid.ai/v1/boards/${boardId}/documents/vectorize`, {
      method: "POST",
      headers: { Authorization: `Bearer ${API_KEY}`, "Content-Type": "application/json" },
      body: JSON.stringify(note),
    })
  ));
  ```

  ```python Python theme={null}
  import concurrent.futures
  research_notes = [
      {"content": "AI agent market notes: enterprise buyers prioritize reliability, governance, and measurable workflow automation over generic copilots.", "metadata": {"source": "research-notes", "topic": "agentic-ai-market"}},
      {"content": "Adoption trend: teams start with internal automation, then expand to customer-facing workflows once observability and approvals are in place.", "metadata": {"source": "analyst-summary", "topic": "enterprise-adoption"}},
  ]
  def vectorize(note):
      return requests.post(f"https://api.chatgrid.ai/v1/boards/{board_id}/documents/vectorize",
          headers={"Authorization": f"Bearer {API_KEY}"}, json=note)
  with concurrent.futures.ThreadPoolExecutor(max_workers=5) as pool:
      list(pool.map(vectorize, research_notes))
  ```
</CodeGroup>

<Note>
  Vectorization runs asynchronously. For large sources, allow 10-30 seconds before querying.
</Note>

## Step 4: Ask AI to synthesize across sources

Create a chat and ask AI for a structured synthesis. It searches all vectorized content before responding.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.chatgrid.ai/v1/boards/{boardId}/chats \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"title": "Research Synthesis"}'

  curl -X POST https://api.chatgrid.ai/v1/boards/{boardId}/chats/{chatId}/messages \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "content": "Synthesize the research on this board into a findings report: 1) Market size and growth, 2) Key players and positioning, 3) Enterprise adoption trends, 4) Gaps and opportunities. Cite which source each finding comes from.",
      "stream": false
    }'
  ```

  ```typescript Node.js theme={null}
  const chat = await fetch(`https://api.chatgrid.ai/v1/boards/${boardId}/chats`, {
    method: "POST",
    headers: { Authorization: `Bearer ${API_KEY}`, "Content-Type": "application/json" },
    body: JSON.stringify({ title: "Research Synthesis" }),
  }).then((r) => r.json());

  const report = await fetch(
    `https://api.chatgrid.ai/v1/boards/${boardId}/chats/${chat.data.id}/messages`,
    {
      method: "POST",
      headers: { Authorization: `Bearer ${API_KEY}`, "Content-Type": "application/json" },
      body: JSON.stringify({
        content: "Synthesize the research on this board into a findings report: 1) Market size and growth, 2) Key players and positioning, 3) Enterprise adoption trends, 4) Gaps and opportunities. Cite which source each finding comes from.",
        stream: false,
      }),
    }
  ).then((r) => r.json());
  ```

  ```python Python theme={null}
  chat = requests.post(f"https://api.chatgrid.ai/v1/boards/{board_id}/chats",
      headers={"Authorization": f"Bearer {API_KEY}"},
      json={"title": "Research Synthesis"}).json()

  report = requests.post(
      f"https://api.chatgrid.ai/v1/boards/{board_id}/chats/{chat['data']['id']}/messages",
      headers={"Authorization": f"Bearer {API_KEY}"},
      json={"content": "Synthesize the research on this board into a findings report: 1) Market size and growth, 2) Key players and positioning, 3) Enterprise adoption trends, 4) Gaps and opportunities. Cite which source each finding comes from.", "stream": False},
  ).json()
  ```
</CodeGroup>

## Step 5: Create findings node and link to sources

Save the synthesis as a notepad, then create edges connecting it to each source for traceability.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.chatgrid.ai/v1/boards/{boardId}/nodes \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"type": "document", "position": {"x": 0, "y": 420}, "data": {"content": "# AI Agent Market Findings\n\n[Paste synthesis here]\n\nGenerated from 5 sources."}}'

  # Link findings to a source node (repeat for each source)
  curl -X POST https://api.chatgrid.ai/v1/boards/{boardId}/edges \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"source_node_id": "{findingsNodeId}", "target_node_id": "{sourceNodeId}", "data": {"label": "sourced from"}}'
  ```

  ```typescript Node.js theme={null}
  const findings = await fetch(`https://api.chatgrid.ai/v1/boards/${boardId}/nodes`, {
    method: "POST",
    headers: { Authorization: `Bearer ${API_KEY}`, "Content-Type": "application/json" },
    body: JSON.stringify({
      type: "document",
      position: { x: 0, y: 420 },
      data: { content: `# AI Agent Market Findings\n\n${report.data.content}\n\nGenerated from ${sourceNodeIds.length} sources.` },
    }),
  }).then((r) => r.json());

  for (const sourceNodeId of sourceNodeIds) {
    await fetch(`https://api.chatgrid.ai/v1/boards/${boardId}/edges`, {
      method: "POST",
      headers: { Authorization: `Bearer ${API_KEY}`, "Content-Type": "application/json" },
      body: JSON.stringify({
        source_node_id: findings.data.id,
        target_node_id: sourceNodeId,
        data: { label: "sourced from" },
      }),
    });
  }
  ```

  ```python Python theme={null}
  findings = requests.post(f"https://api.chatgrid.ai/v1/boards/{board_id}/nodes",
      headers={"Authorization": f"Bearer {API_KEY}"},
      json={"type": "document", "position": {"x": 0, "y": 420}, "data": {
          "content": f"# AI Agent Market Findings\n\n{report['data']['content']}\n\nGenerated from {len(source_node_ids)} sources."
      }}).json()

  for source_node_id in source_node_ids:
      requests.post(f"https://api.chatgrid.ai/v1/boards/{board_id}/edges",
          headers={"Authorization": f"Bearer {API_KEY}"},
          json={"source_node_id": findings["data"]["id"], "target_node_id": source_node_id, "data": {"label": "sourced from"}})
  ```
</CodeGroup>

## What's happening under the hood

The batch endpoint creates all source nodes in a single database transaction. Vectorization runs in parallel on the text you provide, chunks it into searchable segments, and embeds it using pgvector. When AI generates the synthesis, it performs a semantic search across those chunks, retrieves the most relevant passages, and generates a grounded response with citations. Edges are stored as first-class graph relationships, so anyone viewing the board can visually trace a finding back to its original source.

## Next steps

<CardGroup cols={2}>
  <Card title="Client Onboarding" icon="building" href="/cookbooks/client-onboarding">
    Auto-generate knowledge boards for new clients
  </Card>

  <Card title="Sales Deal Room" icon="handshake" href="/cookbooks/sales-deal-room">
    Build deal rooms with competitive positioning
  </Card>
</CardGroup>
