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.

Event Types

Webhook endpoint creation validates event names against the backend enum, but not every enum value is emitted by the current runtime. Agents should only wait for the events in this first table.

EventWhen fired
evaluation.completedA submitted evaluation finished grading
evaluation.failedAn evaluation failed to process
course.deployedA course generation request was completed and the course is ready
course.completedA course generation request failed in the current backend compatibility path
slide.completedA slide deck generation request was completed
video.completedA video scene-generation request was completed. Render is not started by create.
test.deployedA test generation request was completed
test.completedA test generation request failed in the current backend compatibility path
lti.launch_completedAn LTI launch callback was validated and stored
lti.grade_syncedAn LTI grade-sync job successfully posted a score to the LMS
lti.grade_sync_failedAn LTI grade-sync job failed after all retries

Agent rule: for courses and tests, treat course.deployed and test.deployed as the success events. Treat course.completed and test.completed as legacy failure-only compatibility events, despite their names.

These values are accepted by the webhook subscription enum but are reserved or not dispatched by the current backend. Do not block an agent workflow waiting for them:

EventCurrent behavior
assessment.deployedReserved for future assessment deployment events
assessment.submittedReserved for future assessment submission events
slide.deployedReserved; slide generation currently emits slide.completed
video.deployedReserved; video generation currently emits video.completed
lti.launch_failedReserved; current launch errors return HTTP errors and do not emit a webhook

When to Use Webhooks vs Polling

Pick one strategy per resource:

StrategyBest for
WebhooksAsync generation and server-side integrations with a stable inbound HTTPS endpoint.
Polling (GET /v1/platform/{resource}/:id)Sync mode requests. The response already returns the completed result. Polling is only useful if you set mode: "async".

For autonomous agents with no inbound HTTP, polling is simpler. For server-side integrations, webhooks remove the polling overhead.

Video rendering is currently tracked by polling GET /v1/platform/videos/edit/:editToken after calling POST /v1/platform/videos/edit/:editToken/render. The Remotion callback updates renderStatus and videoKey, but it does not emit a separate public webhook event.

Webhook Payload Shapes

Every webhook delivery sends a JSON body specific to the event type. The X-Platform-Event header tells you which event you received. Branch on it.

evaluation.completed

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

evaluation.failed

{
  "evaluationId": "2f4ad455-1e8e-4b6c-9f3a-7ebf4cf6f483",
  "status": "FAILED",
  "error": "Evaluation failed"
}

Synchronous failures may also include evaluationType; async job failures only guarantee evaluationId, status, and error.

course.deployed

{
  "courseRequestId": "uuid",
  "courseId": "uuid",
  "status": "COMPLETED",
  "title": "Python for Beginners",
  "previewUrl": "https://tutorflow.io/en/platform/courses/edit/{editToken}",
  "publicUrl": "https://tutorflow.io/en/platform/courses/{shareToken}/lessons/1"
}

course.completed (failure compatibility event)

{
  "courseRequestId": "uuid",
  "status": "FAILED",
  "error": "Course generation failed"
}

slide.completed

{
  "slideRequestId": "uuid",
  "platformSlideId": "uuid",
  "status": "COMPLETED",
  "previewUrl": "https://tutorflow.io/en/platform/slides/edit/{editToken}"
}

video.completed

Fired when scene generation (script + TTS + clip search) completes. The mp4 does not exist yet. Call POST /v1/platform/videos/edit/:editToken/render, then poll GET /v1/platform/videos/edit/:editToken until renderStatus is COMPLETED and videoKey is populated.

{
  "videoRequestId": "uuid",
  "videoId": "uuid",
  "status": "COMPLETED",
  "previewUrl": "https://tutorflow.io/en/platform/videos/edit/{editToken}"
}

test.deployed

{
  "testRequestId": "uuid",
  "classroomTestId": "uuid",
  "status": "COMPLETED",
  "title": "Basic Algebra Quiz",
  "previewUrl": "https://tutorflow.io/en/platform/tests/{shareToken}",
  "publicUrl": "https://tutorflow.io/en/platform/tests/take/{shareToken}"
}

test.completed (failure compatibility event)

{
  "testRequestId": "uuid",
  "status": "FAILED",
  "error": "Test generation failed"
}

lti.launch_completed

{
  "sessionId": "lti-launch-session-uuid",
  "messageType": "LtiResourceLinkRequest",
  "deploymentId": "lms-deployment-id",
  "resourceLinkId": "lms-resource-link-id",
  "roles": ["http://purl.imsglobal.org/vocab/lis/v2/membership#Learner"],
  "contextId": "lms-course-context-id"
}

lti.grade_synced

{
  "event": "lti.grade_synced",
  "sessionId": "lti-launch-session-uuid",
  "resourceLinkId": "lms-resource-link-id"
}

lti.grade_sync_failed

{
  "event": "lti.grade_sync_failed",
  "sessionId": "lti-launch-session-uuid",
  "error": "AGS score sync failed with 401: invalid token"
}

Signature Verification

Every webhook request includes these headers:

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

The signature is computed as HMAC_SHA256(payload, secret) where secret is the value returned when you created the webhook endpoint. Verify against the raw request body.

const crypto = require('crypto');
 
function verifyWebhook(payload, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .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

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

Managing Webhook Endpoints

Webhook endpoint creation, listing, deletion, and secret rotation are internal dashboard/admin operations in the current MVP. They are intentionally not documented as public agent API surface because Platform API keys cannot call them.

Agents should treat webhook delivery as an operator-configured channel. The agent-facing responsibilities are to store the endpoint secret securely, verify signatures, handle retries idempotently, and process the event payloads listed above.

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.