HomeDocumentation

Webhooks

Telesoft's webhooks allow your application to receive real-time notifications about events that occur in your account. Instead of constantly polling our API for updates, webhooks push data to your application as events happen.

Webhook Overview

Webhooks are HTTP callbacks that are triggered when specific events occur in the Telesoft system. When an event happens that you've subscribed to, Telesoft sends an HTTP POST request to the URL you configured for that event type.

Common Use Cases

  • Receive real-time notifications when a diagnostic analysis is complete
  • Get alerted when new patient data is available for processing
  • Track usage and billing events to maintain accurate records
  • Monitor security events such as API key updates or login attempts
  • Keep your local data synchronized with Telesoft's system

Why use webhooks?

Using webhooks decreases system load and improves performance for both your application and Telesoft's API. Instead of repeatedly querying our API to check if anything has changed, your application can simply wait for notifications about changes, resulting in fewer API calls and more efficient operation.

Setting Up Webhooks

1. Create a Webhook Endpoint

First, create an HTTP endpoint in your application that can receive POST requests from Telesoft. This endpoint should:

  • Be publicly accessible over the internet (or reachable through a proxy)
  • Use HTTPS for security (required for production environments)
  • Respond with a 2xx status code to acknowledge receipt
  • Process the webhook payload asynchronously after acknowledging receipt
// Example Node.js webhook receiver using Express
const express = require('express');
const app = express();
const crypto = require('crypto');

// Parse JSON bodies
app.use(express.json());

app.post('/webhook/telesoft', async (req, res) => {
  // Immediately acknowledge receipt with a 200 OK
  res.status(200).send('Webhook received');
  
  try {
    // Verify the webhook signature (implementation below)
    const isValid = verifySignature(
      req.headers['telesoft-signature'],
      JSON.stringify(req.body),
      process.env.WEBHOOK_SECRET
    );
    
    if (!isValid) {
      console.error('Invalid webhook signature');
      return;
    }
    
    // Process the webhook event based on its type
    const event = req.body;
    
    switch (event.type) {
      case 'diagnostic.complete':
        await handleDiagnosticComplete(event.data);
        break;
      case 'patient.updated':
        await handlePatientUpdated(event.data);
        break;
      // Handle other event types
      default:
        console.log(`Unhandled event type: ${event.type}`);
    }
  } catch (error) {
    console.error('Error processing webhook:', error);
  }
});

app.listen(3000, () => {
  console.log('Webhook server running on port 3000');
});

2. Register Your Webhook URL

Register your webhook URL in the Telesoft Developer Portal or using the API:

// Using the Telesoft SDK
const telesoft = new TelesoftAI({
  apiKey: 'YOUR_API_KEY',
  environment: 'production'
});

// Register a new webhook
const webhook = await telesoft.webhooks.create({
  url: 'https://example.com/webhook/telesoft',
  description: 'Diagnostic events handler',
  events: [
    'diagnostic.complete',
    'diagnostic.failed',
    'patient.created',
    'patient.updated'
  ],
  secret: 'your-webhook-secret'  // Store this securely
});

console.log(`Webhook created with ID: ${webhook.id}`);

You can also create and manage webhooks through the Developer Portal by navigating to Settings > Webhooks.

⚠️ Important

Store your webhook secret securely. This secret is used to verify that webhook events are actually from Telesoft. Never share this secret or commit it to version control.

Securing Webhooks

To ensure that webhook requests are genuinely from Telesoft and haven't been tampered with, we sign each webhook with a signature you can verify.

Verifying Webhook Signatures

Each webhook request includes a Telesoft-Signature header with a timestamp and a signature. Here's how to verify it:

// Node.js example for verifying Telesoft webhook signatures
function verifySignature(signatureHeader, payload, secret) {
  if (!signatureHeader) {
    return false;
  }
  
  // Parse the signature header
  const parts = signatureHeader.split(',');
  const timestamp = parts[0].split('=')[1];
  const signature = parts[1].split('=')[1];
  
  // Check if the timestamp is recent (within 5 minutes)
  const now = Math.floor(Date.now() / 1000);
  if (now - parseInt(timestamp) > 300) {
    console.error('Webhook timestamp too old');
    return false;
  }
  
  // Compute the expected signature
  const signedPayload = `${timestamp}.${payload}`;
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(signedPayload)
    .digest('hex');
  
  // Compare signatures using constant-time comparison
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

Additional Security Recommendations

  • Use HTTPS for your webhook endpoint to encrypt data in transit
  • Implement IP allowlisting if your infrastructure supports it (Telesoft IP ranges are available in our Developer Portal)
  • Process webhooks asynchronously after returning a 200 response to prevent timeouts
  • Implement proper error handling and retries in your webhook processor
  • Use event idempotency keys to prevent duplicate processing

Webhook Event Types

Telesoft supports a wide range of event types that you can subscribe to. Each event type provides specific data related to the event.

Diagnostic Events

Event TypeDescription
diagnostic.createdA new diagnostic analysis has been initiated
diagnostic.completeA diagnostic analysis has completed successfully
diagnostic.failedA diagnostic analysis has failed to complete
diagnostic.updatedA diagnostic record has been updated or revised

Patient Events

Event TypeDescription
patient.createdA new patient record has been created
patient.updatedA patient record has been updated
patient.deletedA patient record has been deleted
patient.mergedTwo patient records have been merged

Account and Billing Events

Event TypeDescription
account.updatedAccount information has been updated
account.api_key.createdA new API key has been created
account.api_key.revokedAn API key has been revoked
billing.subscription.createdA new subscription has been created
billing.subscription.updatedA subscription has been updated
billing.invoice.createdA new invoice has been created
billing.invoice.paidAn invoice has been paid

Sample Webhook Payloads

Diagnostic Complete Event

{
  "id": "evt_9f8e7d6c5b4a",
  "type": "diagnostic.complete",
  "created": 1625097600,
  "idempotency_key": "idk_1a2b3c4d5e6f",
  "data": {
    "diagnostic_id": "diag_7f6e5d4c3b2a",
    "patient_id": "pat_2a3b4c5d6e7f",
    "status": "completed",
    "created_at": "2023-06-30T15:00:00Z",
    "completed_at": "2023-06-30T15:01:23Z",
    "results": {
      "primary_diagnosis": {
        "condition": "Acute Bronchitis",
        "confidence_score": 0.87,
        "icd10_code": "J20.9"
      },
      "differential_diagnoses": [
        {
          "condition": "Common Cold",
          "confidence_score": 0.45,
          "icd10_code": "J00"
        },
        {
          "condition": "Influenza",
          "confidence_score": 0.32,
          "icd10_code": "J10.1"
        }
      ],
      "recommendations": {
        "additional_tests": ["Chest X-ray", "Sputum Culture"],
        "treatment_plan": "Supportive care with rest, hydration, and over-the-counter bronchodilators."
      }
    }
  }
}

Patient Updated Event

{
  "id": "evt_7e6d5c4b3a2",
  "type": "patient.updated",
  "created": 1625184000,
  "idempotency_key": "idk_2b3c4d5e6f7g",
  "data": {
    "patient_id": "pat_2a3b4c5d6e7f",
    "updated_at": "2023-07-01T10:30:00Z",
    "updated_by": "user_5f6g7h8i9j0k",
    "changes": {
      "medical_history": {
        "previous": [
          "Hypertension",
          "Type 2 Diabetes"
        ],
        "current": [
          "Hypertension",
          "Type 2 Diabetes",
          "Asthma"
        ]
      },
      "medications": {
        "previous": [
          {
            "name": "Lisinopril",
            "dosage": "10mg",
            "frequency": "daily"
          },
          {
            "name": "Metformin",
            "dosage": "500mg",
            "frequency": "twice daily"
          }
        ],
        "current": [
          {
            "name": "Lisinopril",
            "dosage": "20mg",
            "frequency": "daily"
          },
          {
            "name": "Metformin",
            "dosage": "500mg",
            "frequency": "twice daily"
          },
          {
            "name": "Albuterol",
            "dosage": "90mcg",
            "frequency": "as needed"
          }
        ]
      }
    }
  }
}

Account API Key Created Event

{
  "id": "evt_5d4c3b2a1z9y",
  "type": "account.api_key.created",
  "created": 1625270400,
  "idempotency_key": "idk_3c4d5e6f7g8h",
  "data": {
    "api_key_id": "key_9y8x7w6v5u4t",
    "name": "Production API Key",
    "created_at": "2023-07-02T08:15:00Z",
    "created_by": "user_5f6g7h8i9j0k",
    "expires_at": "2024-07-02T08:15:00Z",
    "scopes": [
      "diagnostics:read",
      "diagnostics:write",
      "patients:read",
      "patients:write"
    ],
    "last_characters": "t3st"
  }
}

Webhook Best Practices

Idempotency

Telesoft may occasionally send the same webhook event multiple times due to network issues or internal retries. To handle this, each webhook event includes an idempotency_key that remains the same across retries of the same event.

// Example of idempotent webhook processing
async function processWebhook(event) {
  // Check if we've already processed this event
  const processed = await db.webhookEvents.findOne({
    idempotency_key: event.idempotency_key
  });
  
  if (processed) {
    console.log(`Event ${event.id} already processed. Skipping.`);
    return;
  }
  
  // Process the event based on type
  switch (event.type) {
    case 'diagnostic.complete':
      await processDiagnosticResult(event.data);
      break;
    // Handle other event types
  }
  
  // Record that we've processed this event
  await db.webhookEvents.insertOne({
    id: event.id,
    idempotency_key: event.idempotency_key,
    processed_at: new Date()
  });
}

Error Handling

Implement robust error handling in your webhook consumer. If your endpoint encounters an error while processing a webhook, Telesoft will retry the delivery with exponential backoff.

  • Always return a 2xx status code once you've received the webhook, even if you'll process it later
  • Implement a dead-letter queue for events that repeatedly fail processing
  • Monitor webhook failures and set up alerts for persistent issues

Testing Webhooks

Telesoft provides tools to help you test and debug your webhook integration:

  • Use the Developer Portal to manually send test events to your endpoint
  • View webhook delivery history, including request/response details for debugging
  • Set up a separate webhook URL for your development environment

💡 Tip

During development, you can use tools like ngrok or localtunnel to create a temporary public URL that forwards to your local development server, allowing you to test webhooks without deploying to production.