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

# Lab Reports Asynchronous Processing

> Handle lab report analysis with asynchronous processing and real-time webhook notifications

## Asynchronous Processing Overview

```mermaid theme={null}
%%{init: {"theme": "base", "themeVariables": {"primaryColor": "#ffffff", "primaryTextColor": "#161a1d", "primaryBorderColor": "#ded1e4", "lineColor": "#006ad4", "secondaryColor": "#f2f6f9", "tertiaryColor": "#eef1f4", "background": "#ffffff", "mainBkg": "#ffffff", "secondBkg": "#f2f6f9", "actorBkg": "#eef1f4", "actorTextColor": "#161a1d", "actorLineColor": "#006ad4", "signalColor": "#006ad4", "signalTextColor": "#161a1d", "labelBoxBkgColor": "#ffffff", "labelTextColor": "#4f5356", "loopTextColor": "#4f5356", "noteBkgColor": "#f2f6f9", "noteTextColor": "#4f5356", "activationBkgColor": "#ded1e4", "activationBorderColor": "#9ea1a5"}}}%%
sequenceDiagram
    participant A as App
    participant S as Spike API

    A->>S: POST /auth/hmac<br/>HMAC signature
    activate S
    S-->>A: Access Token
    deactivate S

    A->>S: POST /lab_reports<br/>Base64 image + token (wait_on_process: false)
    activate S
    activate S
    S-->>A: 200 OK<br/>record_id, status: "pending"
    deactivate S
    S->>+A: POST webhook URL<br/>Complete lab report data
    deactivate S
    A-->>-S: 200 OK
```

Asynchronous processing allows you to upload lab report documents and receive immediate responses while the AI analysis happens in the background. This approach provides better user experience for interactive applications and enables you to handle multiple requests efficiently.

## How Asynchronous Processing Works

1. **Upload Document** — send a POST request with `wait_on_process: false` (default)
2. **Immediate Response** — receive a response with `status: "processing"`, and a `record_id`
3. **Background Processing** — AI models analyze the document (typically 10–60 seconds)
4. **Get Results** — receive webhook notification with an analysis report when finished

## Getting Results

### Webhooks

Configure a webhook URL to receive real-time notifications when analysis completes:

**Request:**

```json theme={null}
{
  "mime_type": "application/pdf",
  "body": "base64-encoded-document-data",
  "filename": "lab_report_2024_01_15.pdf",
  "wait_on_process": false
}
```

**Immediate Response:**

```json theme={null}
{
  "lab_report": {
    "record_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
    "status": "processing",
    "uploaded_at": "2024-01-15T10:30:04.521Z"
  }
}
```

**Webhook Notification** (when analysis completes):

```json theme={null}
{
  "application_user_id": "end_user_id1",
      "record_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
      "status": "completed",
      "collection_date": "2024-01-15",
      "result_date": "2024-01-16",
      "sections": [
        {
          "loinc_code": "58410-2",
          "loinc_common_name": "CBC panel - Blood by Automated count",
          "original_section_name": "HEMATOLOGY",
          "results": [
            {
              "loinc_code": "6690-2",
              "loinc_common_name": "Leukocytes [#/volume] in Blood",
              "original_test_name": "White Blood Count",
              "original_unit_name": "K/uL",
              "standard_unit_name": "K/uL",
              "value": 7.2,
              "within_normal_range": true
            }
          ]
        }
      ],
      "uploaded_at": "2024-01-15T10:30:04.521Z",
      "modified_at": "2024-01-15T10:30:12.132Z"
}
```

### Alternative: Manual Checking

If webhooks are not available in your environment, you can check the status using the [`GET /lab_reports/{lab_report_id}`](/api-reference/lab-reports-get-lab-report) endpoint:

```
GET /lab_reports/6ba7b810-9dad-11d1-80b4-00c04fd430c8
```

## Webhook Configuration

### Setup

Configure your webhook URL through the [admin console](https://admin.spikeapi.com/). Your endpoint must:

* Respond with HTTP 200 to acknowledge receipt
* Handle POST requests with JSON payloads
* Verify HMAC signatures for security

If requests fail, the system retries **up to 5 times** with exponential backoff.

### Security

All webhook requests include HMAC SHA256 signature verification:

* **Header** — `x-body-signature` contains the hex-encoded HMAC signature
* **Key** — uses your application's webhook signature key (configured in the admin console)
* **Algorithm** — `HMAC-SHA256(webhook_signature_key, request_body)`

See [Webhook Signature](/api-docs/webhooks) for more details.

### Status Handling

Your webhook handler should handle different status values:

* **`completed`** — analysis successful, lab report data are available
* **`failed`** — analysis failed, check `parsing_error` field for details

When `status` is `failed`, check the `parsing_error` field for specific details about what prevented successful analysis.

## Implementation Examples

<CodeGroup>
  ```go [Go] theme={null}
  package main

  import (
  	"crypto/hmac"
  	"crypto/sha256"
  	"encoding/hex"
  	"encoding/json"
  	"fmt"
  	"io"
  	"net/http"
  	"time"
  )

  const webhookSignatureKey = "YOUR_WEBHOOK_SECRET_FROM_ADMIN_CONSOLE"

  // LabReport represents the webhook payload structure
  type LabReport struct {
  	ApplicationUserID string    `json:"application_user_id"`
  	RecordID          string    `json:"record_id"`
  	Status            string    `json:"status"`
  	ParsingError      string    `json:"parsing_error,omitempty"`
  	CollectionDate    string    `json:"collection_date,omitempty"`
  	UploadedAt        time.Time `json:"uploaded_at"`
  	ModifiedAt        time.Time `json:"modified_at"`
  	// Add other fields as needed
  }

  func main() {
  	http.HandleFunc("/lab-reports-webhook", func(w http.ResponseWriter, r *http.Request) {
  		// Verify HMAC signature
  		signature := r.Header.Get("x-body-signature")
  		if signature == "" {
  			http.Error(w, "Missing signature", http.StatusBadRequest)
  			return
  		}

  		// Read the request body
  		body, err := io.ReadAll(r.Body)
  		if err != nil {
  			http.Error(w, "Failed to read body", http.StatusInternalServerError)
  			return
  		}

  		// Calculate and verify HMAC
  		hm := hmac.New(sha256.New, []byte(webhookSignatureKey))
  		hm.Write(body)
  		expectedSignature := hex.EncodeToString(hm.Sum(nil))
  		
  		if signature != expectedSignature {
  			http.Error(w, "Invalid signature", http.StatusUnauthorized)
  			return
  		}

  		// Parse lab report
  		var record LabReport
  		if err := json.Unmarshal(body, &record); err != nil {
  			http.Error(w, "Invalid JSON", http.StatusBadRequest)
  			return
  		}

  		// Process the lab report
  		fmt.Printf("Received lab report analysis: %s for user %s\n", record.Status, record.ApplicationUserID)
  		if record.Status == "completed" {
  			fmt.Printf("Lab report completed for record %s\n", record.RecordID)
  			// Update your application with the results
  		} else if record.Status == "failed" {
  			fmt.Printf("Analysis failed: %s\n", record.ParsingError)
  			// Handle failure case
  		}

  		// Respond with success
  		w.WriteHeader(http.StatusOK)
  		w.Write([]byte("OK"))
  	})

  	fmt.Println("Starting lab reports webhook server on port 8000")
  	http.ListenAndServe(":8000", nil)
  }
  ```

  ```javascript [JavaScript] theme={null}
  const crypto = require("crypto");
  const express = require("express");

  const WEBHOOK_SIGNATURE_KEY = "YOUR_WEBHOOK_SECRET_FROM_ADMIN_CONSOLE";
  const app = express();

  // Middleware to capture raw body for signature verification
  app.use('/lab-reports-webhook', express.raw({type: 'application/json'}));

  app.post('/lab-reports-webhook', (req, res) => {
    const signature = req.headers['x-body-signature'];
    
    if (!signature) {
      return res.status(400).send('Missing signature');
    }

    // Verify HMAC signature
    const hmac = crypto.createHmac('sha256', WEBHOOK_SIGNATURE_KEY);
    hmac.update(req.body);
    const expectedSignature = hmac.digest('hex');
    
    if (signature !== expectedSignature) {
      return res.status(401).send('Invalid signature');
    }

    // Parse lab report
    try {
      const labReport = JSON.parse(req.body);
      
      console.log(`Received lab report analysis: ${labReport.status} for user ${labReport.application_user_id}`);
      
      if (labReport.status === 'completed') {
        console.log(`Lab report completed: ${labReport.record_id}`);
        
        // Update your application with the results
        updateLabReportData(labReport);
        
      } else if (labReport.status === 'failed') {
        console.log(`Analysis failed: ${labReport.parsing_error}`);
        
        // Handle failure case
        handleAnalysisFailure(labReport.record_id, labReport.application_user_id);
      }
      
      res.status(200).send('OK');
    } catch (error) {
      res.status(400).send('Invalid JSON');
    }
  });

  function updateLabReportData(labReport) {
    // Your application logic to store/process the lab report data
    console.log('Updating lab report data...');
  }

  function handleAnalysisFailure(recordId, applicationUserId) {
    // Your application logic to handle failed analysis
    console.log('Handling analysis failure...');
  }

  app.listen(8000, () => {
    console.log('Lab reports webhook server running on port 8000');
  });
  ```

  ```python [Python] theme={null}
  import hmac
  import hashlib
  import json
  from datetime import datetime
  from flask import Flask, request, Response

  app = Flask(__name__)
  WEBHOOK_SIGNATURE_KEY = "YOUR_WEBHOOK_SECRET_FROM_ADMIN_CONSOLE"

  def verify_signature(body: bytes, signature: str) -> bool:
      """Verify the HMAC signature of the request body."""
      expected_signature = hmac.new(
          WEBHOOK_SIGNATURE_KEY.encode('utf-8'),
          body,
          hashlib.sha256
      ).hexdigest()
      return hmac.compare_digest(signature, expected_signature)

  @app.route('/lab-reports-webhook', methods=['POST'])
  def handle_lab_report_webhook():
      # Get signature from headers
      signature = request.headers.get('x-body-signature')
      if not signature:
          return Response('Missing signature', status=400)
      
      # Get raw request body
      body = request.get_data()
      
      # Verify signature
      if not verify_signature(body, signature):
          return Response('Invalid signature', status=401)
      
      # Parse lab report
      try:
          lab_report = json.loads(body)
          
          print(f"Received lab report analysis: {lab_report['status']} for user {lab_report['application_user_id']}")
          
          if lab_report['status'] == 'completed':
              print(f"Lab report completed: {lab_report['record_id']}")
              
              # Update your application with the results
              update_lab_report_data(lab_report)
              
          elif lab_report['status'] == 'failed':
              print(f"Analysis failed: {lab_report.get('parsing_error', 'Unknown error')}")
              
              # Handle failure case
              handle_analysis_failure(lab_report['record_id'], lab_report['application_user_id'])
          
          return Response('OK', status=200)
          
      except json.JSONDecodeError:
          return Response('Invalid JSON', status=400)

  def update_lab_report_data(lab_report):
      """Update your application with the lab report analysis results."""
      print("Updating lab report data...")
      # Your application logic here

  def handle_analysis_failure(record_id, application_user_id):
      """Handle failed lab report analysis."""
      print("Handling analysis failure...")
      # Your application logic here

  if __name__ == '__main__':
      print('Starting lab reports webhook server on port 8000')
      app.run(host='0.0.0.0', port=8000)
  ```

  ```php [PHP] theme={null}
  <?php

  class LabReportWebhook {
      private const WEBHOOK_SIGNATURE_KEY = 'YOUR_WEBHOOK_SECRET_FROM_ADMIN_CONSOLE';
      
      public function handleRequest() {
          // Verify HMAC signature
          $signature = $_SERVER['HTTP_X_BODY_SIGNATURE'] ?? '';
          if (empty($signature)) {
              http_response_code(400);
              echo 'Missing signature';
              return;
          }

          // Read request body
          $body = file_get_contents('php://input');
          if ($body === false) {
              http_response_code(500);
              echo 'Failed to read body';
              return;
          }

          // Calculate and verify HMAC
          $expectedSignature = hash_hmac('sha256', $body, self::WEBHOOK_SIGNATURE_KEY);
          if ($signature !== $expectedSignature) {
              http_response_code(401);
              echo 'Invalid signature';
              return;
          }

          // Parse lab report
          $labReport = json_decode($body, true);
          if (json_last_error() !== JSON_ERROR_NONE) {
              http_response_code(400);
              echo 'Invalid JSON';
              return;
          }

          // Process the lab report
          echo "Received lab report analysis: {$labReport['status']} for user {$labReport['application_user_id']}\n";
          
          if ($labReport['status'] === 'completed') {
              echo "Lab report completed: {$labReport['record_id']}\n";
              
              // Update your application with the results
              $this->updateLabReportData($labReport);
              
          } elseif ($labReport['status'] === 'failed') {
              $parsingError = $labReport['parsing_error'] ?? 'Unknown error';
              echo "Analysis failed: {$parsingError}\n";
              
              // Handle failure case
              $this->handleAnalysisFailure($labReport['record_id'], $labReport['application_user_id']);
          }

          http_response_code(200);
          echo 'OK';
      }
      
      private function updateLabReportData($labReport) {
          // Your application logic to store/process the lab report data
          echo "Updating lab report data...\n";
      }
      
      private function handleAnalysisFailure($recordId, $applicationUserId) {
          // Your application logic to handle failed analysis
          echo "Handling analysis failure...\n";
      }
  }

  // Handle the webhook request
  $webhook = new LabReportWebhook();
  $webhook->handleRequest();
  ?>
  ```
</CodeGroup>

For complete API specifications and additional configuration options, see the [Implementation Guide](/lab-reports/implementation).
