Webhooks

Receive real-time notifications when events happen in your account.

Overview

Webhooks allow your application to receive real-time HTTP notifications when events occur, such as when an evaluation completes. Instead of polling the API, you register a URL and we'll send events to it.

Supported Events

EventDescription
evaluation.completedAn evaluation has been graded successfully
evaluation.failedAn evaluation failed to process
assessment.deployedReserved for future assessment deployment events
assessment.submittedReserved for future assessment submission events
course.deployedReserved for future course deployment events
course.completedReserved for future course completion events

Webhook Payload

{
  "evaluationId": "2f4ad455-1e8e-4b6c-9f3a-7ebf4cf6f483",
  "status": "COMPLETED",
  "evaluationType": "open_ended",
  "score": 8,
  "maxScore": 10,
  "normalizedScore": 0.8
}

Signature Verification

Every webhook request includes these headers:

  • X-Platform-Signature
  • X-Platform-Event

The current backend computes the signature as HMAC_SHA256(payload, SHA256(secret)). Verify against the raw request body.

const crypto = require('crypto');
 
function verifyWebhook(payload, signature, secret) {
  const signingKey = crypto.createHash('sha256').update(secret).digest('hex');
  const expected = crypto
    .createHmac('sha256', signingKey)
    .update(payload)
    .digest('hex');
 
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

Retry Policy

If your endpoint returns a non-2xx status code, we retry with exponential backoff:

AttemptDelay
1st retry5 seconds
2nd retry25 seconds
3rd retry125 seconds

After the initial delivery attempt plus 3 retries (4 total attempts), the delivery is marked as failed.

Managing Webhook Endpoints

Use the management endpoints below to create, list, and delete webhook subscriptions. All endpoints require a Platform API Key via the Authorization header.

Create a Webhook Endpoint

POST /v1/platform/webhooks
ParameterTypeRequiredDescription
urlstringYesThe HTTPS URL that will receive webhook events
eventsstring[]YesList of event types to subscribe to
secretstringNoA shared secret for HMAC signature verification. Auto-generated if omitted.
curl -X POST https://api.tutorflow.io/v1/platform/webhooks \
  -H "Authorization: Bearer tf_platform_..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://myapp.com/webhooks/tutorflow",
    "events": ["evaluation.completed", "evaluation.failed"]
  }'

Response:

{
  "id": "wh_abc123",
  "url": "https://myapp.com/webhooks/tutorflow",
  "events": ["evaluation.completed", "evaluation.failed"],
  "secret": "whsec_...",
  "createdAt": "2026-03-19T10:00:00Z"
}

Store the secret value securely. It is only returned once at creation time and is used for signature verification.

List Webhook Endpoints

GET /v1/platform/webhooks

Returns all webhook endpoints registered for your workspace.

curl https://api.tutorflow.io/v1/platform/webhooks \
  -H "Authorization: Bearer tf_platform_..."

Response:

{
  "data": [
    {
      "id": "wh_abc123",
      "url": "https://myapp.com/webhooks/tutorflow",
      "events": ["evaluation.completed", "evaluation.failed"],
      "createdAt": "2026-03-19T10:00:00Z"
    }
  ]
}

Delete a Webhook Endpoint

DELETE /v1/platform/webhooks/:id
ParameterTypeDescription
idstringThe webhook endpoint ID
curl -X DELETE https://api.tutorflow.io/v1/platform/webhooks/wh_abc123 \
  -H "Authorization: Bearer tf_platform_..."

Returns 204 No Content on success.

Best Practices

  • Always verify the HMAC signature before processing.
  • Return 200 OK quickly and process the event asynchronously.
  • Handle duplicate events idempotently using the payload resource identifier.
  • Use HTTPS endpoints only.