Use webhooks after the polling flow works. Webhooks notify your system when a Content Integration job finishes or fails.
Events
| Event | Trigger |
|---|---|
content.completed | A job reached completed. |
content.failed | A job reached failed. |
Create webhook
curl -X POST "https://api.tutorflow.io/v1/content/organizations/{organizationId}/webhooks" \
-H "Content-Type: application/json" \
-b tutorflow-admin.cookies \
-d '{
"url": "https://example.com/tutorflow/content-webhooks",
"events": ["content.completed", "content.failed"]
}'Response:
{
"id": "2f083d42-5c0f-4de6-9c3f-35c4823a589e",
"organizationId": "00000000-0000-4000-8000-000000000001",
"url": "https://example.com/tutorflow/content-webhooks",
"events": ["content.completed", "content.failed"],
"status": "ACTIVE",
"secret": "generated-signing-secret"
}The secret is shown once. Store it in your webhook receiver.
Request headers
X-Content-Integration-Event: content.completed
X-Content-Integration-Signature: hmac_sha256_signature
Content-Type: application/jsonReceiver behavior
- Verify the signature.
- Return a 2xx response quickly.
- Use the job id in the payload to fetch the result endpoint.
- Treat repeated deliveries as duplicates and handle them idempotently.
Node.js signature verification
import crypto from 'node:crypto'
function verifyContentSignature(rawBody, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex')
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected),
)
}List webhooks
curl "https://api.tutorflow.io/v1/content/organizations/{organizationId}/webhooks" \
-b tutorflow-admin.cookiesList responses do not include the signing secret.
Delete webhook
curl -X DELETE "https://api.tutorflow.io/v1/content/organizations/{organizationId}/webhooks/{webhookId}" \
-b tutorflow-admin.cookiesRetry handling
TutorFlow retries failed deliveries. Your receiver should:
- Return 2xx only after accepting the event.
- Avoid side effects before signature verification.
- Store processed event ids or job ids to prevent duplicate processing.
- Fetch the result endpoint if a webhook payload is not enough for your workflow.