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
| Event | Description |
|---|---|
evaluation.completed | An evaluation has been graded successfully |
evaluation.failed | An evaluation failed to process |
assessment.deployed | Reserved for future assessment deployment events |
assessment.submitted | Reserved for future assessment submission events |
course.deployed | Reserved for future course deployment events |
course.completed | Reserved 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-SignatureX-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:
| Attempt | Delay |
|---|---|
| 1st retry | 5 seconds |
| 2nd retry | 25 seconds |
| 3rd retry | 125 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| Parameter | Type | Required | Description |
|---|---|---|---|
url | string | Yes | The HTTPS URL that will receive webhook events |
events | string[] | Yes | List of event types to subscribe to |
secret | string | No | A 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/webhooksReturns 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| Parameter | Type | Description |
|---|---|---|
id | string | The 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 OKquickly and process the event asynchronously. - Handle duplicate events idempotently using the payload resource identifier.
- Use HTTPS endpoints only.