Skip to main content

Overview

The High IQ API uses Server-Sent Events (SSE) for real-time streaming of AI-generated report sections. Reports are generated section-by-section, with each section streamed as a series of events: start, partial, complete, or error. This allows the client to render content progressively as it is generated. The streaming service is built on Hono SSE with AI SDK 6 streamText() and Output.object() for structured output with partial updates.

Endpoints

EndpointMethodDescription
/api/v1/reports/stream/healthGETHealth check for streaming service
/api/v1/reports/stream/sectionsGETList available section keys
/api/v1/reports/stream/:sectionKeyPOSTStream a single report section
/api/v1/reports/stream/batchPOSTStream multiple sections in sequence

Single Section Streaming

Stream a single report section by its key. Returns an SSE stream with progressive updates.

Request

curl -X POST "https://tiwih-api.vercel.app/api/v1/reports/stream/terpene_analysis" \
  -H "Content-Type: application/json" \
  -H "Accept: text/event-stream" \
  -d '{
    "orderItems": [
      {
        "strainName": "Blue Dream",
        "grams": 3.5,
        "type": "hybrid",
        "thcPct": 21,
        "terpenes": [
          { "name": "Myrcene", "percentage": 0.45 },
          { "name": "Caryophyllene", "percentage": 0.32 }
        ]
      }
    ],
    "orderMetadata": {
      "dispensaryName": "Green Leaf",
      "orderDate": "2026-02-15",
      "totalSpent": 45.00
    }
  }'

Request Body Schema

{
  orderItems?: {
    strainName: string;
    grams: number;
    price?: number;
    thcPct?: number;
    cbdPct?: number;
    type?: 'sativa' | 'indica' | 'hybrid';
    terpenes?: { name: string; percentage: number }[];
    effects?: string[];
    aromas?: string[];
  }[];
  orderMetadata?: {
    dispensaryName?: string;
    orderDate?: string;
    totalSpent?: number;
  };
  previousSections?: Record<string, unknown>;
}

SSE Event Stream

The server sends events in the standard SSE format. Each event has a type, sectionKey, and optional data:
event: start
id: a1b2c3d4-uuid
data: {"type":"start","sectionKey":"terpene_analysis","estimatedTokens":500}

event: partial
id: a1b2c3d4-uuid
data: {"type":"partial","sectionKey":"terpene_analysis","data":{"title":"Terpene Profile"},"progress":25}

event: partial
id: a1b2c3d4-uuid
data: {"type":"partial","sectionKey":"terpene_analysis","data":{"title":"Terpene Profile","summary":"Your order features..."},"progress":60}

event: complete
id: a1b2c3d4-uuid
data: {"type":"complete","sectionKey":"terpene_analysis","data":{"title":"Terpene Profile","summary":"Your order features a myrcene-dominant profile...","insights":["..."]},"progress":100}

Batch Section Streaming

Stream multiple sections in sequence. Each section is generated one after another, with overall progress tracking. Previously generated sections are passed as context to dependent sections.

Request

curl -X POST "https://tiwih-api.vercel.app/api/v1/reports/stream/batch" \
  -H "Content-Type: application/json" \
  -H "Accept: text/event-stream" \
  -d '{
    "sectionKeys": ["overview", "terpene_analysis", "effects_breakdown"],
    "orderItems": [
      {
        "strainName": "OG Kush",
        "grams": 7,
        "type": "indica",
        "thcPct": 24
      }
    ]
  }'

Batch Request Schema

{
  sectionKeys: string[];  // At least one section key required
  orderItems?: OrderItem[];
  orderMetadata?: OrderMetadata;
}

Batch Event Stream

Batch streaming adds batch_start and batch_complete events, plus overall progress tracking on each section event:
event: batch_start
id: req-uuid
data: {"type":"batch_start","totalSections":3,"sectionKeys":["overview","terpene_analysis","effects_breakdown"]}

event: start
id: req-uuid-overview
data: {"type":"start","sectionKey":"overview","overallProgress":0,"completedSections":0,"totalSections":3}

event: partial
id: req-uuid-overview
data: {"type":"partial","sectionKey":"overview","progress":50,"overallProgress":16,"completedSections":0,"totalSections":3}

event: complete
id: req-uuid-overview
data: {"type":"complete","sectionKey":"overview","data":{...},"progress":100,"overallProgress":33,"completedSections":1,"totalSections":3}

event: start
id: req-uuid-terpene_analysis
data: {"type":"start","sectionKey":"terpene_analysis","overallProgress":33,"completedSections":1,"totalSections":3}

...

event: batch_complete
id: req-uuid
data: {"type":"batch_complete","totalSections":3,"completedSections":3,"results":{"overview":{...},"terpene_analysis":{...},"effects_breakdown":{...}}}

Event Types

EventDescriptionFields
batch_startBatch streaming has beguntotalSections, sectionKeys
startIndividual section generation startedsectionKey, estimatedTokens
partialPartial structured data availablesectionKey, data (partial), progress (0-100)
completeSection fully generatedsectionKey, data (complete), progress (100)
errorSection generation failedsectionKey, error (message)
batch_completeAll sections finishedtotalSections, completedSections, results

Batch-Enriched Fields

In batch mode, every section event includes additional fields for overall progress:
FieldTypeDescription
overallProgressnumber0-100 percentage across all sections
completedSectionsnumberCount of finished sections
totalSectionsnumberTotal sections in the batch

StreamEvent Interface

interface StreamEvent {
  type: 'start' | 'partial' | 'complete' | 'error';
  sectionKey: string;
  data?: unknown;        // Partial or complete structured data
  progress?: number;     // 0-100 section progress
  text?: string;         // Raw text output
  error?: string;        // Error message (type: 'error' only)
  estimatedTokens?: number; // Estimated token count (type: 'start' only)
}

Available Sections

Fetch the list of available section keys and their metadata:
curl "https://tiwih-api.vercel.app/api/v1/reports/stream/sections"
{
  "sections": [
    {
      "key": "overview",
      "label": "Overview",
      "tab": "summary",
      "order": 1,
      "strategy": "ai",
      "estimatedTokens": 500
    },
    {
      "key": "terpene_analysis",
      "label": "Terpene Analysis",
      "tab": "science",
      "order": 2,
      "strategy": "ai",
      "estimatedTokens": 800
    }
  ],
  "totalSections": 12
}
Sections with strategy: "computed" are handled client-side and cannot be streamed. The API returns a message indicating the section should be computed locally.

Client Integration

JavaScript / TypeScript (EventSource)

const response = await fetch(
  'https://tiwih-api.vercel.app/api/v1/reports/stream/batch',
  {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Accept': 'text/event-stream',
    },
    body: JSON.stringify({
      sectionKeys: ['overview', 'terpene_analysis'],
      orderItems: [{ strainName: 'Blue Dream', grams: 3.5 }],
    }),
  }
);

const reader = response.body!.getReader();
const decoder = new TextDecoder();

let buffer = '';
while (true) {
  const { done, value } = await reader.read();
  if (done) break;

  buffer += decoder.decode(value, { stream: true });
  const lines = buffer.split('\n');
  buffer = lines.pop() || '';

  for (const line of lines) {
    if (line.startsWith('data: ')) {
      const event = JSON.parse(line.slice(6));

      switch (event.type) {
        case 'start':
          console.log(`Starting ${event.sectionKey}...`);
          break;
        case 'partial':
          // Update UI with partial data
          updateSection(event.sectionKey, event.data, event.progress);
          break;
        case 'complete':
          // Section finished
          finalizeSection(event.sectionKey, event.data);
          break;
        case 'error':
          console.error(`Error in ${event.sectionKey}: ${event.error}`);
          break;
      }
    }
  }
}

React Native (High IQ Mobile)

The mobile app uses custom hooks for SSE consumption:
import { useStreamingReportGeneration } from '@/_hooks/useStreamingReportGeneration';

function ReportScreen({ orderItems }) {
  const {
    sections,
    overallProgress,
    isStreaming,
    error,
    startGeneration,
  } = useStreamingReportGeneration();

  const handleGenerate = () => {
    startGeneration({
      sectionKeys: ['overview', 'terpene_analysis', 'effects_breakdown'],
      orderItems,
    });
  };

  return (
    <View>
      <ProgressBar progress={overallProgress} />
      {Object.entries(sections).map(([key, section]) => (
        <StreamingSectionCard
          key={key}
          sectionKey={key}
          data={section.data}
          progress={section.progress}
          status={section.status}
        />
      ))}
    </View>
  );
}

Error Handling

If a section fails during streaming, an error event is sent for that section and the batch continues to the next section. The batch_complete event includes the count of successfully completed sections.
event: error
id: req-uuid-failing_section
data: {"type":"error","sectionKey":"failing_section","error":"AI model timeout","overallProgress":66,"completedSections":2,"totalSections":3}
If the streaming service itself is unavailable, the API returns a 503 response before streaming begins:
{
  "error": "Streaming service is not available",
  "code": "SERVICE_UNAVAILABLE"
}

Health Check

Verify the streaming service is available before initiating a stream:
curl "https://tiwih-api.vercel.app/api/v1/reports/stream/health"
{
  "available": true,
  "service": "section-streaming",
  "timestamp": "2026-02-16T12:00:00.000Z",
  "aiSectionsCount": 12
}
Check available: true before starting a report generation flow. If false, AI models may not be configured or the service is temporarily unavailable.