API Reference

Webhooks

Receive real-time notifications about events in your Caring CourseForge account

Webhooks allow you to receive real-time HTTP notifications when events occur in your account, eliminating the need for polling and enabling responsive integrations.

Available Events

Course Events

  • course.created - New course created
  • course.updated - Course details modified
  • course.published - Course published/unpublished
  • course.deleted - Course deleted

Student Events

  • student.enrolled - Student enrolled in course
  • student.completed_lesson - Lesson completed
  • student.completed_course - Course completed
  • student.unenrolled - Student unenrolled

Assessment Events

  • quiz.submitted - Quiz submitted
  • quiz.passed - Quiz passed
  • quiz.failed - Quiz failed

System Events

  • export.completed - Course export finished
  • export.failed - Export failed

Creating a Webhook

Setup Steps

  1. Go to Settings → Webhooks
  2. Click "Create Webhook"
  3. Enter your endpoint URL (must be HTTPS)
  4. Select events you want to receive
  5. Optionally set a custom secret for signature verification
  6. Click "Create"
  7. Test with "Send Test Event"

Webhook Payload

Standard Payload Structure

{
  "id": "evt_1234567890",
  "type": "student.completed_course",
  "created": 1633024800,
  "data": {
    "student_id": "stu_abc123",
    "student_email": "student@example.com",
    "course_id": "course_xyz789",
    "course_title": "Introduction to Python",
    "completed_at": "2025-10-11T14:30:00Z",
    "final_score": 92.5,
    "time_spent_minutes": 480
  },
  "account_id": "acct_demo"
}

Verifying Webhook Signatures

Verify that webhooks are from Caring CourseForge and haven't been tampered with:

Signature Header

Each webhook includes a signature in the X-CCF-Signature header

X-CCF-Signature: t=1633024800,v1=5257a869e7ecebeda32...

Node.js Verification

const crypto = require('crypto');

function verifyWebhook(payload, signature, secret) {
  const [timestamp, hash] = signature.split(',')
    .map(part => part.split('=')[1]);

  const signedPayload = `${timestamp}.${JSON.stringify(payload)}`;
  const expectedHash = crypto
    .createHmac('sha256', secret)
    .update(signedPayload)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(hash),
    Buffer.from(expectedHash)
  );
}

Python Verification

import hmac
import hashlib
import json

def verify_webhook(payload, signature, secret):
    parts = dict(part.split('=') for part in signature.split(','))
    timestamp = parts['t']
    received_hash = parts['v1']

    signed_payload = f"{timestamp}.{json.dumps(payload)}"
    expected_hash = hmac.new(
        secret.encode(),
        signed_payload.encode(),
        hashlib.sha256
    ).hexdigest()

    return hmac.compare_digest(received_hash, expected_hash)

Handling Webhooks

Best Practices:
  • Return 200 OK as quickly as possible (within 5 seconds)
  • Process webhook data asynchronously (use queues)
  • Implement idempotency (same event may be sent multiple times)
  • Always verify signatures
  • Log all webhook events for debugging
  • Handle duplicate events gracefully

Retry Logic

Retry Schedule:

If your endpoint doesn't return 200-299, we'll retry:

  • Immediately
  • After 5 minutes
  • After 30 minutes
  • After 2 hours
  • After 6 hours
Automatic Disabling:

After 5 consecutive failures, the webhook is automatically disabled. You'll receive an email notification.

Testing Webhooks

Local Development

Use tools like ngrok to test webhooks locally:

# Start ngrok tunnel
ngrok http 3000
# Use the ngrok URL as your webhook endpoint
https://abc123.ngrok.io/webhooks/courseforge

Monitoring Webhooks

View webhook activity in your dashboard:

  • All webhook deliveries (success/failed)
  • Response status codes
  • Response times
  • Retry attempts
  • Payload and response body

Common Issues

Endpoint Timing Out

Solution: Return 200 immediately, then process asynchronously. Don't perform long-running tasks in the webhook handler.

Receiving Duplicate Events

Solution: Use the event id to implement idempotency. Store processed event IDs and skip duplicates.

Webhook Disabled Unexpectedly

Solution: Check webhook logs in dashboard. Ensure your endpoint returns 2xx status and responds within 5 seconds.

Rate Limits

Webhooks are not subject to API rate limits, but there are delivery limits:

Maximum webhooks per account:10 (Professional), 50 (Enterprise)
Maximum events per minute:1,000

What's Next?