Real-Time Event Notifications
Subscribe to webhook events to get instant notifications when drug prices change, recalls are issued, generics become available, and more. No polling required.
Quick Setup
Get webhook notifications flowing to your application in three steps.
POST /v1/webhooks
{
"url": "https://yourapp.com/api/webhooks/tm",
"events": ["price.changed", "drug.recalled"],
"description": "Production webhook"
}// Response includes your signing secret:
{
"id": "wh_abc123",
"secret": "whsec_a1b2c3d4e5f6...",
"url": "https://yourapp.com/api/webhooks/tm",
"events": ["price.changed", "drug.recalled"],
"active": true
}// Express.js example
app.post("/api/webhooks/tm", (req, res) => {
const signature = req.headers["x-tm-signature"];
const timestamp = req.headers["x-tm-timestamp"];
if (!verifySignature(req.body, signature, timestamp)) {
return res.status(401).send("Invalid signature");
}
const event = req.body;
switch (event.event) {
case "price.changed":
handlePriceChange(event.data);
break;
case "drug.recalled":
handleRecall(event.data);
break;
}
res.status(200).json({ received: true });
});Available Events
Subscribe to any combination of the following events. You can update your subscriptions at any time via the API or dashboard.
Triggered when the price of a monitored drug changes at any pharmacy. Includes the old price, new price, percentage change, and the pharmacy details.
Payload Schema
{
"event": "price.changed",
"id": "evt_a1b2c3d4e5f6",
"created_at": "2026-04-05T14:30:00Z",
"data": {
"drug": {
"id": "drug_12345",
"name": "Metformin 500mg",
"din": "02248573"
},
"pharmacy": {
"id": "pharm_67890",
"name": "Shoppers Drug Mart #1234",
"city": "Toronto",
"province": "ON"
},
"old_price": 12.99,
"new_price": 10.49,
"change_amount": -2.50,
"change_percent": -19.25,
"currency": "CAD",
"effective_date": "2026-04-05"
}
}Triggered when Health Canada issues a recall for a drug in the database. Includes recall classification, affected lots, and recommended actions.
Payload Schema
{
"event": "drug.recalled",
"id": "evt_r3c4l5a6b7c8",
"created_at": "2026-04-05T09:15:00Z",
"data": {
"drug": {
"id": "drug_54321",
"name": "Valsartan 80mg",
"din": "02303310"
},
"recall": {
"id": "recall_hc_2026_0412",
"classification": "Type I",
"reason": "Potential NDMA contamination above acceptable limits",
"affected_lots": ["LOT2025A", "LOT2025B", "LOT2025C"],
"manufacturer": "Generic Pharma Inc.",
"health_canada_url": "https://recalls-rappels.canada.ca/en/alert-recall/...",
"action_required": "Stop dispensing affected lots. Patients should contact their pharmacist.",
"issued_date": "2026-04-05"
}
}
}Triggered when a new generic alternative becomes available for a brand-name drug. Includes pricing comparison and the list of pharmacies that carry it.
Payload Schema
{
"event": "generic.available",
"id": "evt_g1e2n3r4i5c6",
"created_at": "2026-04-05T11:00:00Z",
"data": {
"brand_drug": {
"id": "drug_11111",
"name": "Lipitor 20mg",
"din": "02230711",
"average_price": 89.99
},
"generic_drug": {
"id": "drug_22222",
"name": "Atorvastatin 20mg",
"din": "02401142",
"manufacturer": "Apotex Inc.",
"average_price": 24.99,
"savings_percent": 72.2
},
"available_at_pharmacies": 1247,
"provinces_available": ["ON", "BC", "AB", "QC", "MB", "SK", "NS", "NB"]
}
}Triggered when pharmacy details change, such as hours, contact information, services offered, or when a pharmacy opens or closes permanently.
Payload Schema
{
"event": "pharmacy.updated",
"id": "evt_p1h2r3m4u5p6",
"created_at": "2026-04-05T16:45:00Z",
"data": {
"pharmacy": {
"id": "pharm_67890",
"name": "Shoppers Drug Mart #1234",
"city": "Toronto",
"province": "ON"
},
"changes": [
{
"field": "hours.saturday",
"old_value": "09:00-18:00",
"new_value": "09:00-21:00"
},
{
"field": "services",
"added": ["flu_vaccination", "covid_testing"],
"removed": []
}
],
"updated_at": "2026-04-05T16:45:00Z"
}
}Triggered when a drug shortage is reported or updated. Includes severity level, estimated resolution date, and alternative medications if available.
Payload Schema
{
"event": "shortage.reported",
"id": "evt_s1h2o3r4t5g6",
"created_at": "2026-04-05T08:30:00Z",
"data": {
"drug": {
"id": "drug_33333",
"name": "Amoxicillin 250mg/5mL Suspension",
"din": "00628018"
},
"shortage": {
"id": "shortage_2026_0405",
"severity": "critical",
"status": "active",
"reason": "Manufacturing delay",
"affected_provinces": ["ON", "QC", "BC", "AB"],
"estimated_resolution": "2026-05-15",
"alternatives": [
{ "din": "02412345", "name": "Amoxicillin 500mg Capsules" },
{ "din": "02412346", "name": "Cephalexin 250mg/5mL Suspension" }
],
"reported_date": "2026-04-05",
"source": "Drug Shortages Canada"
}
}
}Retry Policy
If your endpoint returns a non-2xx status code or times out (30 second limit), we will retry delivery using an exponential backoff schedule. After all retries are exhausted, the event is marked as failed and available in your dashboard for manual replay.
| Attempt | Delay |
|---|---|
#1 | Immediate |
#2 | 1 minute |
#3 | 5 minutes |
#4 | 30 minutes |
#5 | 2 hours |
#6 | 8 hours |
#7 | 24 hours |
Events that fail all 7 retry attempts are stored for 30 days and can be manually replayed from your developer dashboard.
Security
Every webhook request is signed with your unique signing secret. Always verify signatures before processing events.
Headers Included
- X-TM-SignatureHMAC-SHA256 hex digest
- X-TM-TimestampUnix timestamp (seconds)
- X-TM-EventEvent type name
import crypto from "crypto";
function verifySignature(
payload: string,
signature: string,
timestamp: string,
secret: string
): boolean {
// Reject requests older than 5 minutes
const now = Math.floor(Date.now() / 1000);
if (Math.abs(now - parseInt(timestamp)) > 300) {
return false;
}
const signedPayload = `${timestamp}.${payload}`;
const expected = crypto
.createHmac("sha256", secret)
.update(signedPayload)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}Start Receiving Events
Set up your first webhook in under 5 minutes. All plans include webhook access with unlimited event subscriptions.