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

# Send causal chat message

> Stream a causal analysis response for a given question using Server-Sent Events.

Send a causal question to the Wu-Weism reasoning engine. The response is streamed as a sequence of Server-Sent Events (SSE), allowing you to display incremental progress — domain classification, SCM loading, intervention evaluation, and answer text — as they complete.

<Info>
  This endpoint uses `text/event-stream`. You must read the response body as a stream and parse individual SSE frames. Standard `fetch` with a body reader works in all modern environments.
</Info>

## Endpoint

```
POST /api/causal-chat
```

## Authentication

<Warning>
  Authentication is required on every request. Unauthenticated requests return `401 Unauthorized`.
</Warning>

Include either a session cookie (set automatically after sign-in) or a Bearer token in the `Authorization` header:

```
Authorization: Bearer <token>
```

## Request

**Content-Type:** `application/json`

### Headers

<ParamField header="Authorization" type="string" required>
  Bearer token for API authentication. Format: `Bearer <token>`.
</ParamField>

<ParamField header="Content-Type" type="string" required>
  Must be `application/json`.
</ParamField>

<ParamField header="X-BYOK-Api-Key" type="string">
  Bring-your-own-key: pass your own API key for the selected AI provider. When present, this key is forwarded to the upstream provider instead of Wu-Weism's managed key. Required if your account is configured for BYOK mode.
</ParamField>

### Body parameters

<ParamField body="question" type="string" required>
  The causal question to analyze. Should be phrased as a causal query — for example, "Does X causally affect Y?" or "What would happen if we intervene on Z?". Cannot be empty.
</ParamField>

<ParamField body="messages" type="object[]">
  Previous conversation turns for multi-turn sessions. Each element must include `role` and `content`.

  <Expandable title="message object properties">
    <ParamField body="messages[].role" type="string" required>
      Speaker role. One of `"user"` or `"assistant"`.
    </ParamField>

    <ParamField body="messages[].content" type="string" required>
      Message text content.
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="sessionId" type="string">
  UUID identifying a persistent session. When provided, the server associates this request with the session's stored history and SCM context. If omitted, the request is treated as stateless.
</ParamField>

<ParamField body="intervention" type="object">
  Do-calculus intervention specification for Rung 2 (interventional) analysis. Requires `operatorMode` to be set to `"intervene"`.

  <Expandable title="intervention object properties">
    <ParamField body="intervention.node_id" type="string" required>
      The identifier of the node in the Structural Causal Model (SCM) on which to apply the intervention (the `do(·)` operator).
    </ParamField>

    <ParamField body="intervention.value" type="number" required>
      The value to assign to `node_id` under the intervention.
    </ParamField>

    <ParamField body="intervention.outcome_var" type="string">
      The target outcome variable whose post-intervention distribution is of interest.
    </ParamField>

    <ParamField body="intervention.adjustment_set" type="string[]">
      Explicit set of variables to condition on for backdoor adjustment. If omitted, the engine selects a valid adjustment set automatically.
    </ParamField>

    <ParamField body="intervention.known_confounders" type="string[]">
      Variables known to confound the relationship between `node_id` and `outcome_var`. Used to inform identifiability analysis.
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="providerId" type="string" default="anthropic">
  AI provider to use for language model inference. Accepted values: `"anthropic"`, `"openai"`, `"gemini"`. Defaults to `"anthropic"` when omitted.
</ParamField>

<ParamField body="model" type="string">
  Specific model ID within the selected provider — for example, `"claude-opus-4-5"` or `"gpt-4o"`. When omitted, the provider's default model is used. Must be a valid model for the chosen `providerId`; mismatches return a `400` error.
</ParamField>

<ParamField body="operatorMode" type="string">
  Activates Rung 2 analysis mode. Accepted values:

  * `"intervene"` — evaluate the causal effect of a `do(·)` intervention.
  * `"audit"` — audit the causal graph for identifiability and policy compliance without applying an intervention.
</ParamField>

<ParamField body="attachments" type="object[]">
  PDF documents to include as context for the analysis. Each attachment is base64-encoded.

  <Expandable title="attachment object properties">
    <ParamField body="attachments[].name" type="string" required>
      Human-readable filename, e.g. `"study.pdf"`.
    </ParamField>

    <ParamField body="attachments[].data" type="string" required>
      Base64-encoded file content.
    </ParamField>

    <ParamField body="attachments[].mimeType" type="string" required>
      MIME type of the attachment. Use `"application/pdf"` for PDF files.
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="modelKey" type="string">
  Override the automatic domain classifier and load a specific SCM model by its key. Useful when you know exactly which causal model applies to your question. When omitted, the domain is classified automatically from `question`.
</ParamField>

<ParamField body="traceId" type="string">
  Trace identifier for counterfactual provenance. Associates this request with a prior causal trace for counterfactual reasoning or audit replay.
</ParamField>

## Response

**Content-Type:** `text/event-stream`

The response body is a stream of SSE frames. Each frame has the form:

```
event: <event_name>
data: <json_payload>

```

Events arrive in roughly the order listed below, though not all events are emitted on every request — for example, `intervention_gate` and `intervention_effect` only appear when an intervention is specified.

### SSE events

<ResponseField name="thinking" type="object">
  Emitted periodically during processing to indicate progress. Display to users as a loading indicator.

  <Expandable title="payload">
    <ResponseField name="message" type="string">
      Human-readable status message, e.g. `"Loading causal model..."`.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="domain_classified" type="object">
  The domain classifier has identified the subject area of the question.

  <Expandable title="payload">
    <ResponseField name="primary" type="string">
      Primary domain label, e.g. `"neuroscience"`.
    </ResponseField>

    <ResponseField name="confidence" type="number">
      Classification confidence score between `0` and `1`.
    </ResponseField>

    <ResponseField name="secondary" type="string[]">
      Additional candidate domain labels ranked by relevance.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="scm_loaded" type="object">
  The Structural Causal Model for the identified domain has been loaded.

  <Expandable title="payload">
    <ResponseField name="tier1" type="object">
      Tier-1 SCM structure (core nodes and edges).
    </ResponseField>

    <ResponseField name="tier2" type="object">
      Tier-2 SCM structure (extended variables and mechanisms).
    </ResponseField>

    <ResponseField name="model" type="object">
      <Expandable title="model properties">
        <ResponseField name="modelKey" type="string">
          Unique key identifying the loaded SCM.
        </ResponseField>

        <ResponseField name="version" type="string">
          Version string of the SCM, e.g. `"2.1.0"`.
        </ResponseField>
      </Expandable>
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="intervention_gate" type="object">
  Gate evaluation result for the requested intervention. Only emitted when `operatorMode` is `"intervene"` or `"audit"`.

  <Expandable title="payload">
    <ResponseField name="treatment" type="string">
      The treatment node that was evaluated.
    </ResponseField>

    <ResponseField name="outcome" type="string">
      The outcome variable that was evaluated.
    </ResponseField>

    <ResponseField name="allowed" type="boolean">
      Whether the intervention is permitted under current policy.
    </ResponseField>

    <ResponseField name="allowedOutputClass" type="string">
      Output class label for the permitted intervention.
    </ResponseField>

    <ResponseField name="rationale" type="string">
      Explanation of why the intervention was allowed or denied.
    </ResponseField>

    <ResponseField name="identifiability" type="string">
      Identifiability status — for example, `"identifiable"` or `"non-identifiable"`.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="intervention_effect" type="object">
  The computed causal effect of the intervention. Only emitted when `operatorMode` is `"intervene"` and the gate allows the operation.

  <Expandable title="payload">
    <ResponseField name="do" type="object[]">
      List of applied do-calculus operations.

      <Expandable title="do item properties">
        <ResponseField name="nodeName" type="string">
          Name of the intervened node.
        </ResponseField>

        <ResponseField name="value" type="number">
          Value assigned to the node under intervention.
        </ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="affected" type="string[]">
      List of variable names whose distributions are affected downstream.
    </ResponseField>

    <ResponseField name="latency_ms" type="number">
      Time in milliseconds taken to compute the intervention effect.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="answer_chunk" type="object">
  A chunk of the generated answer text. These events are emitted incrementally — concatenate `text` values in order to reconstruct the full response.

  <Expandable title="payload">
    <ResponseField name="text" type="string">
      Partial answer text.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="model_fallback" type="object">
  The requested model was unavailable and a substitute was used.

  <Expandable title="payload">
    <ResponseField name="provider" type="string">
      The AI provider that handled the request.
    </ResponseField>

    <ResponseField name="requested_model" type="string">
      The model ID that was originally requested.
    </ResponseField>

    <ResponseField name="used_model" type="string">
      The model ID that was actually used.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="web_grounding_completed" type="object">
  Web sources were retrieved and used to ground the causal analysis.

  <Expandable title="payload">
    <ResponseField name="sourceCount" type="number">
      Total number of sources found.
    </ResponseField>

    <ResponseField name="sources" type="object[]">
      Array of source objects. Each source includes URL, title, and relevance metadata.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="common_sense_policy" type="object">
  Safety policy evaluation result.

  <Expandable title="payload">
    <ResponseField name="action" type="string">
      Policy decision: `"allow"` or `"decline"`. When `"decline"`, the stream ends without an `answer_chunk` or `complete` event.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="complete" type="object">
  Final event indicating the stream has ended successfully.

  <Expandable title="payload">
    <ResponseField name="finished" type="boolean">
      Always `true`.
    </ResponseField>
  </Expandable>
</ResponseField>

## Error responses

Errors are returned as standard JSON responses before the stream is opened.

| Status | Body                                                                   | Description                                                                                             |
| ------ | ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| `401`  | —                                                                      | Missing or invalid authentication credentials.                                                          |
| `400`  | `{"error": "Question is required"}`                                    | The `question` field was empty or missing.                                                              |
| `400`  | `{"error": "Invalid providerId"}`                                      | `providerId` is not one of `"anthropic"`, `"openai"`, `"gemini"`.                                       |
| `400`  | `{"error": "Invalid model for selected provider"}`                     | `model` is not valid for the specified `providerId`.                                                    |
| `500`  | `{"error": "Configuration Error: Missing API key for provider '...'"}` | The selected provider has no configured API key. Pass your key via `X-BYOK-Api-Key` or contact support. |

## Examples

### Basic question

<CodeGroup>
  ```javascript JavaScript (fetch) theme={null}
  const response = await fetch('https://wuweism.com/api/causal-chat', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer <token>',
    },
    body: JSON.stringify({
      question: 'Does cortisol exposure causally increase hippocampal volume reduction?',
      sessionId: 'my-session-uuid',
      providerId: 'anthropic',
    }),
  });

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

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    const text = decoder.decode(value);
    // Each SSE frame has the form:
    // "event: answer_chunk\ndata: {"text":"..."}\n\n"
    console.log(text);
  }
  ```

  ```bash cURL theme={null}
  curl --request POST \
    --url https://wuweism.com/api/causal-chat \
    --header 'Authorization: Bearer <token>' \
    --header 'Content-Type: application/json' \
    --no-buffer \
    --data '{
      "question": "Does cortisol exposure causally increase hippocampal volume reduction?",
      "sessionId": "my-session-uuid",
      "providerId": "anthropic"
    }'
  ```
</CodeGroup>

### Do-calculus intervention

Use `operatorMode: "intervene"` together with the `intervention` object to evaluate a causal effect under a hypothetical manipulation.

```json theme={null}
{
  "question": "What would happen if we intervene to reduce cortisol?",
  "sessionId": "my-session-uuid",
  "providerId": "anthropic",
  "operatorMode": "intervene",
  "intervention": {
    "node_id": "cortisol_level",
    "value": 0.3,
    "outcome_var": "hippocampal_volume",
    "known_confounders": ["stress_exposure", "age"],
    "adjustment_set": ["stress_exposure"]
  }
}
```

### Parsing SSE events

A minimal parser for the SSE stream in JavaScript:

```javascript theme={null}
function parseSSEFrame(raw) {
  const lines = raw.split('\n');
  let eventName = '';
  let dataLine = '';

  for (const line of lines) {
    if (line.startsWith('event: ')) eventName = line.slice(7).trim();
    if (line.startsWith('data: ')) dataLine = line.slice(6).trim();
  }

  if (!eventName || !dataLine) return null;

  return { event: eventName, data: JSON.parse(dataLine) };
}

// Usage inside the reader loop:
const frame = parseSSEFrame(decoder.decode(value));
if (frame?.event === 'answer_chunk') {
  process.stdout.write(frame.data.text);
}
if (frame?.event === 'complete') {
  console.log('\nStream finished.');
}
```

<Note>
  SSE frames are separated by a blank line (`\n\n`). A single `read()` call may return multiple frames or a partial frame depending on network conditions. For production use, buffer incoming chunks and split on `\n\n` before parsing.
</Note>
