Skip to main content
Back to Guides

Webhook Configuration

Receive real-time event notifications from Thought Industries

Webhooks let your application react to platform events in real time — enrollments, course completions, purchases, and more. Instead of polling APIs, you configure an HTTPS endpoint and Thought Industries will POST event payloads to it as they happen.

Prerequisites

A publicly accessible HTTPS endpoint

Ability to verify HMAC-SHA256 signatures

Admin access to Thought Industries webhook settings

A server that can respond within 5 seconds

Create a Webhook Endpoint

You can register webhooks via the admin panel or programmatically via the REST API:

POST /incoming/v2/webhooks
POST /incoming/v2/webhooks
Authorization: Bearer {API_KEY}
Content-Type: application/json

{
  "url": "https://your-app.com/webhooks/ti",
  "events": [
    "enrollment.created",
    "course.completed",
    "order.completed"
  ],
  "secret": "whsec_your_webhook_secret",
  "active": true
}

Verify Signatures

Every webhook request includes an X-TI-Signature header. Always verify it before processing the payload to prevent replay attacks and spoofing:

webhook-handler.js
const crypto = require('crypto');

function verifyWebhook(body, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(body, 'utf8')
    .digest('hex');

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

// Express middleware example
app.post('/webhooks/ti', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-ti-signature'];
  if (!verifyWebhook(req.body, signature, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }
  const event = JSON.parse(req.body);
  // Process event...
  res.status(200).send('OK');
});

Use crypto.timingSafeEqual to prevent timing attacks. Never use simple string comparison for signature validation.

Handle Retries

If your endpoint returns a non-2xx status or times out (30s), the platform retries with exponential backoff over 24 hours:

AttemptDelay
1st retry1 minute
2nd retry5 minutes
3rd retry30 minutes
4th retry2 hours
5th retry24 hours

After 5 failed attempts, the event is marked undeliverable. Consistently failing endpoints may be automatically disabled. Return 200 immediately and process asynchronously.

Event Types

Common events you can subscribe to. See the Webhooks documentation for the full event catalog and payload schemas.

enrollment.createdLearner enrolled in content
course.completedLearner finished all required items
course.progress_updatedProgress percentage changed
certificate.issuedCertificate generated on completion
user.createdNew account registered
order.completedPurchase transaction finalized

Best Practices

Return 200 quickly

Respond within 5 seconds. Use a message queue for heavy processing.

Handle duplicates

Events may be delivered more than once. Use idempotency keys based on event ID + timestamp.

Use HTTPS only

The platform refuses to deliver to HTTP endpoints. TLS 1.2 or higher is required.

Monitor delivery logs

Check the admin panel for failed deliveries and retry status.