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 Type | Description |
---|---|
diagnostic.created | A new diagnostic analysis has been initiated |
diagnostic.complete | A diagnostic analysis has completed successfully |
diagnostic.failed | A diagnostic analysis has failed to complete |
diagnostic.updated | A diagnostic record has been updated or revised |
Patient Events
Event Type | Description |
---|---|
patient.created | A new patient record has been created |
patient.updated | A patient record has been updated |
patient.deleted | A patient record has been deleted |
patient.merged | Two patient records have been merged |
Account and Billing Events
Event Type | Description |
---|---|
account.updated | Account information has been updated |
account.api_key.created | A new API key has been created |
account.api_key.revoked | An API key has been revoked |
billing.subscription.created | A new subscription has been created |
billing.subscription.updated | A subscription has been updated |
billing.invoice.created | A new invoice has been created |
billing.invoice.paid | An 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.