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

# Stream Processing Progress — GET /process/{documentId}

> GET /process/{documentId} streams pipeline progress as Server-Sent Events. Each event carries a stage and status. Reconnection replays missed events.

After uploading a document, connect to `GET /process/{documentId}` to receive real-time progress over a Server-Sent Events (SSE) stream. The server emits one event per pipeline stage as it starts, completes, or fails. When the entire pipeline finishes, the server sends a final `complete` event containing the full recovery plan and closes the connection.

## Path parameter

<ParamField path="documentId" type="string (UUID)" required>
  The document identifier returned by `POST /upload`. The stream is scoped to this document and to the authenticated user who uploaded it.
</ParamField>

## Pipeline stages

AfterCare processes your document through eight sequential stages. You receive a `started` event at the beginning of each stage and either a `completed` or `failed` event when it finishes.

<Steps>
  <Step title="ocr">
    Extracts raw text from the uploaded PDF or image using optical character recognition.
  </Step>

  <Step title="extract">
    Parses the raw OCR text into structured sections (diagnoses, discharge instructions, follow-up notes).
  </Step>

  <Step title="meds">
    Identifies all medications, dosages, frequencies, and administration instructions.
  </Step>

  <Step title="appts">
    Locates follow-up appointment details including dates, specialists, and locations.
  </Step>

  <Step title="warnings">
    Extracts warning signs and the recommended action for each (call provider, go to emergency room, call 911).
  </Step>

  <Step title="timeline">
    Builds a day-by-day recovery timeline from the discharge instructions.
  </Step>

  <Step title="explain">
    Generates plain-language explanations for medical terms found in the document.
  </Step>

  <Step title="judge">
    Validates all extracted data for consistency and flags low-confidence items for review.
  </Step>
</Steps>

## Event shape

Each SSE message carries a named event (the `event:` line) matching the pipeline stage, and a `data:` line containing a JSON payload with the following fields:

```json theme={null}
{
  "stage": "meds",
  "status": "completed",
  "documentId": "550e8400-e29b-41d4-a716-446655440000",
  "timestamp": "2024-09-14T08:01:23.456Z",
  "data": {}
}
```

| Field | Type | Description |
| - | - | - |
| `stage` | `PipelineStage` | One of: `ocr`, `extract`, `meds`, `appts`, `warnings`, `timeline`, `explain`, `judge` |
| `status` | `string` | `"started"`, `"completed"`, or `"failed"` |
| `documentId` | `string` | The document this event belongs to. Useful when multiplexing multiple streams. |
| `timestamp` | `string` | ISO 8601 timestamp of when the event was emitted. |
| `data` | `object` | Stage-specific payload. Present on `completed` events. |
| `error` | `StructuredAiError` | Only present when `status` is `"failed"`. Contains `code`, `message`, and `retryable`. |

<Note>
  A `failed` status on a single stage means AfterCare could not populate **that section** of the recovery plan — the `data` for that stage will be empty. The pipeline continues running the remaining stages, and the final `complete` event is still emitted. A fully failed document (all stages failed or processing errored out) sends a top-level `failed` event instead of `complete`.
</Note>

## Terminal events

After all stages finish, the server emits one of two named terminal events and closes the stream:

* **`event: complete`** — `data` contains the full `RecoveryPlan` object.
* **`event: failed`** — `data` contains a `StructuredAiError` with `code`, `message`, and `retryable`.

The server also sends periodic `: heartbeat` comment lines to keep the connection alive through proxies and load balancers. Your parser should ignore lines that start with `:`.

## Reconnection and event history

The server stores every event emitted for a document. If your connection drops, simply reconnect to the same URL — the server will **replay all previously emitted events** before resuming the live stream. If processing has already finished, the server replays the full history, sends the terminal event, and closes the connection immediately.

<Warning>
  Do **not** use the browser's native `EventSource` API to consume this stream. `EventSource` cannot attach an `Authorization` header, so the server will reject the connection with `401`. Use `fetch()` with a streaming reader instead, as shown in the example below.
</Warning>

## Code example

```typescript TypeScript (fetch streaming reader) theme={null}
async function streamProgress(
  documentId: string,
  token: string,
  onEvent: (event: { stage: string; status: string; data: unknown }) => void,
): Promise<void> {
  const response = await fetch(
    `https://api.aftercare.app/process/${documentId}`,
    {
      headers: {
        Authorization: `Bearer ${token}`,
        Accept: "text/event-stream",
      },
    },
  );

  if (!response.ok) {
    throw new Error(`Stream request failed: ${response.status}`);
  }

  const reader = response.body!.getReader();
  const decoder = new TextDecoder();
  let buffer = "";
  let currentEventName = "";

  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) {
      // Capture the named event type (e.g. "meds", "complete", "failed")
      if (line.startsWith("event: ")) {
        currentEventName = line.slice(7).trim();
        continue;
      }

      if (line.startsWith("data: ")) {
        // Skip SSE comment heartbeats
        if (line.startsWith(": ")) continue;

        const payload = JSON.parse(line.slice(6));

        if (currentEventName === "complete") {
          console.log("Recovery plan ready:", payload);
          return;
        }

        if (currentEventName === "failed") {
          throw new Error(`Pipeline failed: ${payload.message}`);
        }

        // Pipeline stage event
        if (payload.stage) {
          onEvent(payload);
        }

        currentEventName = "";
      }
    }
  }
}
```

## StructuredAiError shape

When a stage or the entire document fails, the `error` field (or the terminal `failed` event data) contains:

<ResponseField name="code" type="string">
  One of `AI_PROVIDER_CONFIG_MISSING`, `AI_PROVIDER_OUTAGE`, `AI_PROVIDER_UNAVAILABLE`, or `AI_VALIDATION_FAILED`.
</ResponseField>

<ResponseField name="message" type="string">
  A human-readable description of the failure.
</ResponseField>

<ResponseField name="retryable" type="boolean">
  When `true`, the failure is transient and re-uploading the document may succeed. When `false`, the error is a permanent validation failure and retrying will not help.
</ResponseField>

## Error responses

| Status | Meaning |
| - | - |
| `401` | Missing or invalid Bearer token. |
| `404` | No document found for the given `documentId`, or the document does not belong to the authenticated user. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.