Skip to main content
Helios delivers analysis results to your webhook URL asynchronously. This guide covers webhook payloads, signature verification, and best practices.

How Webhooks Work

When you submit a request to the Helios API:
  1. You receive an immediate response with a runId
  2. Analysis runs in the background (5-8 minutes for Light Agent, 10-13 minutes for Deep Agent, 3-6 minutes for Lab Results Agent)
  3. Results are delivered to your webhookUrl via HTTP POST
  4. Your endpoint should return 200 OK to acknowledge receipt

Webhook Headers

Every webhook includes these headers for security and tracking:
HeaderDescriptionExample
X-Helios-SignatureHMAC-SHA256 signaturesha256=a1b2c3...
X-Helios-TimestampUnix timestamp (seconds)1705320000
X-Helios-Run-IDUnique run identifier550e8400-e29b-...
Content-TypeAlways JSONapplication/json

Webhook Payloads

Success Payload (Light Agent)

When Light Agent analysis completes successfully:
{
  "runId": "550e8400-e29b-41d4-a716-446655440000",
  "status": "completed",
  "agent": "light",
  "timestamp": "2025-01-15T12:01:30.000Z",
  "sequenceNumber": 7,
  "result": {
    "selectedElements": [
      {
        "item": "Hemoglobin A1c",
        "category": "Laboratory Value",
        "patient_value": "7.2%",
        "matching_reason": "Elevated HbA1c indicates suboptimal glycemic control"
      }
    ],
    "patientResponse": "Your recent lab results show...",
    "clinicianResponse": "The patient presents with...",
    "citations": {
      "patient": {
        "1": {
          "url": "https://pubmed.ncbi.nlm.nih.gov/12345678/",
          "title": "Glycemic Control in Type 2 Diabetes",
          "metadata": { "type": "pubmed" }
        }
      },
      "clinician": {
        "1": {
          "url": "https://pubmed.ncbi.nlm.nih.gov/87654321/",
          "title": "ADA Standards of Care 2025",
          "metadata": { "type": "pubmed" }
        }
      }
    }
  }
}

Success Payload (Deep Agent)

Deep Agent responses include additional research task information:
{
  "runId": "550e8400-e29b-41d4-a716-446655440000",
  "status": "completed",
  "agent": "deep",
  "timestamp": "2025-01-15T12:15:30.000Z",
  "sequenceNumber": 9,
  "result": {
    "selectedElements": [
      {
        "item": "Hemoglobin A1c",
        "category": "Laboratory Value",
        "patient_value": "7.2%",
        "matching_reason": "Elevated HbA1c indicates suboptimal glycemic control"
      }
    ],
    "patientResponse": "Your recent lab results show...",
    "clinicianResponse": "The patient presents with...",
    "researchTasks": [
      "Research current ADA guidelines for glycemic control in Type 2 Diabetes",
      "Investigate medication intensification strategies for patients with HbA1c > 7%",
      "Analyze cardiovascular risk factors and preventive measures"
    ],
    "citations": {
      "patient": { "1": { "url": "...", "title": "..." } },
      "clinician": { "1": { "url": "...", "title": "..." } }
    }
  }
}

Success Payload (Lab Results Agent)

Lab Results Agent responses include the analyzed labs and metadata:
{
  "runId": "550e8400-e29b-41d4-a716-446655440000",
  "status": "completed",
  "agent": "lab-results",
  "timestamp": "2025-01-15T12:05:00.000Z",
  "sequenceNumber": 8,
  "result": {
    "labResults": [
      {
        "name": "Creatinine",
        "value": "1.8 mg/dL",
        "flag": "High",
        "referenceRange": "0.7-1.3 mg/dL",
        "date": "2025-01-15",
        "category": "Renal"
      }
    ],
    "patientResponse": "Your kidney function tests show...",
    "clinicianResponse": "The patient demonstrates Stage 3b CKD...",
    "citations": {
      "patient": { "1": { "url": "...", "title": "..." } },
      "clinician": { "1": { "url": "...", "title": "..." } }
    },
    "metadata": {
      "labsReceived": 15,
      "labsAnalyzed": 15,
      "insightsMatched": 8
    }
  }
}
Lab Results Agent does not include selectedElements since all provided labs are analyzed directly. Instead, it includes labResults (the labs that were analyzed) and metadata with processing statistics.

Error Payload

When analysis fails:
{
  "runId": "550e8400-e29b-41d4-a716-446655440000",
  "status": "error",
  "timestamp": "2025-01-15T12:01:30.000Z",
  "sequenceNumber": 3,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid health data format"
  }
}

Status Update Payload

During processing, you receive status updates at each step:
{
  "runId": "550e8400-e29b-41d4-a716-446655440000",
  "status": "processing",
  "agent": "light",
  "step": 2,
  "stepName": "Analyzing health data",
  "timestamp": "2025-01-15T12:00:45.000Z",
  "sequenceNumber": 2
}
Important: The sequenceNumber field is a monotonically increasing number that allows you to order webhooks correctly. Webhooks may arrive out of order due to network conditions, but you can sort them by sequenceNumber to reconstruct the correct sequence. The timestamp field can also be used as a fallback for ordering.

Processing Steps

The following steps are sent during processing: Light Agent Steps:
StepName
1Processing started
2Analyzing health data
3Selecting relevant elements
4Researching medical literature
5Generating patient response
6Generating clinician response
7Finalizing results
8Delivering final results
Deep Agent Steps:
StepNameDescription
1Processing startedRequest received and validated
2Analyzing health dataExtracting insights from EHR data
3Selecting relevant elementsAI selecting important health elements
4Routing to Deep AgentHanding off to research agent
5Starting research tasksBeginning parallel literature research
6Research completeAll research tasks finished
7Generating responsesCreating clinician and patient reports
8Finalizing resultsPreparing final output
Steps 1-4 are sent by the initial API server. Steps 5-8 and the final result are sent by the Deep Agent research processor. This architecture allows the Deep Agent to run for up to 16 minutes without timeout constraints.
Lab Results Agent Steps:
StepName
1Processing started
2Analyzing lab results
3Prioritizing findings
4Generating search queries
5Searching medical literature
6Processing search results
7Generating responses
8Final webhook delivered

Error Codes

CodeDescription
VALIDATION_ERRORInvalid request data
AUTH_ERRORAuthentication failed
RATE_LIMIT_ERRORToo many requests
CONCURRENCY_LIMIT_ERRORToo many concurrent requests
VERTEX_AI_ERRORAI service error
LITSENSE_ERRORLiterature search error
EXA_ERRORResearch search error
FIREBASE_ERRORDeep Agent processing error
ELEMENT_SELECTION_ERRORFailed to select relevant health elements
CLINICIAN_RESPONSE_ERRORFailed to generate clinician response
PATIENT_RESPONSE_ERRORFailed to generate patient response
TIMEOUT_ERRORAnalysis timed out
INTERNAL_ERRORServer error
NO_DATAInsufficient health data provided

Signature Verification

Always verify webhook signatures in production to ensure requests are authentic and haven’t been tampered with.

Verification Steps

  1. Get the raw request body - Use the raw body string, not parsed JSON
  2. Check the timestamp - Reject webhooks older than 5 minutes
  3. Compute the expected signature - sha256=HMAC-SHA256(webhook_secret, raw_body)
  4. Compare signatures - Use timing-safe comparison

Implementation Examples

import { createHmac, timingSafeEqual } from 'crypto';

function verifyWebhook(rawBody, signature, timestamp, webhookSecret) {
  // Check timestamp is recent (within 5 minutes)
  const now = Math.floor(Date.now() / 1000);
  const webhookTime = parseInt(timestamp, 10);
  if (Math.abs(now - webhookTime) > 300) {
    console.error('Webhook timestamp too old');
    return false;
  }

  // Compute expected signature
  const expected = 'sha256=' + createHmac('sha256', webhookSecret)
    .update(rawBody)
    .digest('hex');

  // Timing-safe comparison
  if (expected.length !== signature.length) {
    return false;
  }

  return timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  );
}

// Express.js example
app.post('/webhook/helios', express.raw({ type: 'application/json' }), (req, res) => {
  const rawBody = req.body.toString();
  const signature = req.headers['x-helios-signature'];
  const timestamp = req.headers['x-helios-timestamp'];

  if (!verifyWebhook(rawBody, signature, timestamp, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }

  const payload = JSON.parse(rawBody);
  // Process webhook...
  
  res.status(200).send('OK');
});

Retry Policy

Helios retries failed webhook deliveries with exponential backoff:
AttemptDelayTotal Time
1Immediate0s
25 seconds5s
315 seconds20s
After 3 failed attempts, the webhook is logged but not retried further.
Your webhook endpoint should return a 2xx status code within 30 seconds to be considered successful.

Best Practices

Process webhooks asynchronously. Return 200 OK immediately, then handle the data in a background job.
app.post('/webhook/helios', (req, res) => {
  // Acknowledge immediately
  res.status(200).send('OK');
  
  // Process in background
  processWebhookAsync(req.body);
});
In rare cases, you may receive the same webhook twice. Use the runId to deduplicate:
const processedRuns = new Set();

if (processedRuns.has(payload.runId)) {
  return; // Already processed
}
processedRuns.add(payload.runId);
Webhooks may arrive out of order due to network conditions. Use the sequenceNumber field to sort them correctly:
const webhooks = [];

// Store webhooks as they arrive
app.post('/webhook/helios', (req, res) => {
  const payload = req.body;
  webhooks.push(payload);
  
  // Sort by sequenceNumber to get correct order
  webhooks.sort((a, b) => (a.sequenceNumber || 0) - (b.sequenceNumber || 0));
  
  res.status(200).send('OK');
});
The sequenceNumber starts at 1 and increments for each webhook sent during a run. The final completed or error webhook will have the highest sequenceNumber.
Log all incoming webhooks for debugging and audit purposes. Store the runId, status, and timestamp.
Your webhook endpoint must use HTTPS in production. Self-signed certificates are not supported.

Getting Your Webhook Secret

Your webhook secret is used to verify webhook signatures and ensure requests are authentic. To generate your webhook secret:
  1. Log in to your Helios Dashboard
  2. Go to Settings (API Keys page)
  3. In the Webhook Secret section, click Generate Secret
  4. Copy and securely store the secret—it will only be shown once
If you regenerate your webhook secret, the previous secret is immediately invalidated. Make sure to update your server configuration before regenerating.

Next Steps