Playground ↗ Postman ↗ Dashboard ↗

Webhook event types

Every webhook delivery has this envelope:

{
  "event": "course.completed",
  "data": { /* event-specific payload, see below */ }
}

Plus three headers — see Register webhook → Verifying deliveries.

Deliveries are retried with exponential backoff (1m, 5m, 30m, 2h, 12h) on any non-2xx response or network error. After the 5th failure, the webhook is marked inactive — you’ll need to re-register it.

course.completed

Fires when a course finishes generating successfully — every chapter is committed, study guide is built, and the course’s status flips to completed.

{
  "event": "course.completed",
  "data": {
    "job_id": "j_01HW0XYZ…",
    "course_id": "8fa640c0-22e6-436b-becd-bf2cfc86741a",
    "title": "Photosynthesis: From Light to Life",
    "chapters_count": 5,
    "source_type": "topic"
  }
}

source_type is one of topic, files, youtube, web, mixed.

course.failed

Fires when a course generation job exits in error and is not auto-resumable.

{
  "event": "course.failed",
  "data": {
    "job_id": "j_01HW0XYZ…",
    "error": {
      "code": "LLM_TIMEOUT",
      "message": "Upstream LLM did not respond within 120s during chapter 3.",
      "request_id": "req_2YmW…dXk"
    }
  }
}

Common code values: LLM_TIMEOUT, LLM_RATE_LIMIT, LLM_REFUSED, VECTOR_DB_UNAVAILABLE, INTERNAL.

You can call POST /v2/courses/{course_id}/resume on the partially built course to retry the missing chapters.

job.progress (reserved)

This event type is accepted at registration time, but is not currently emitted in production. It’s reserved for fine-grained per-chapter progress updates in a future release. If you need progress today, poll GET /v2/courses/{course_id}/progress.

test

Fires when you POST /v2/webhooks/{webhook_id}/test — a hand-triggered delivery for verifying your endpoint and signature handling.

{
  "event": "test",
  "data": {
    "message": "This is a test delivery from the Nuton webhook system.",
    "webhook_id": "wh_01HW0XYZ…"
  }
}