ClonifyDevelopersOpen panel

Getting started

  • Authentication
  • Errors
  • Pagination
  • Rate limits
  • Webhooks
  • For AI agents
  • MCP server

API reference

  • Student learning activity6
  • External API deliveries6
  • Chat with the clone1
  • Contacts & tags5
  • Academy access4
  • Conversations4
  • Product catalogue11
  • External sales2
  • Knowledge12
  • Multimedia4
  • Clones (versions in the API)3
  • Meetings & bookings3
  • Webinars6
  • Account & usage1
Clonify

Student learning activity

GET/api/v1/products/{id}/students

List a course's students and how they're doing

Who has access to the course and each student's status: active (some study signal in the last 7 days), at_risk (7 to 13 days without activity), stalled (14 or more), not_started (no activity at all) or completed (every published lesson marked). Activity means completing a lesson, opening it, watching its video, attending one of the course's live sessions or watching its recording. Includes the last activity with its lesson or live session, completed and started-but-unmarked lessons, access details and a summary of the whole course. Same data as the Students tab in the panel.

Query parameters

statusactive | at_risk | stalled | not_started | completed | attention

Only one status; attention = students who need a nudge (at risk, stalled for under 90 days, and not started 7 to 29 days after getting access).

sortattention | recent | oldest | progress | progress-asc | activity | activity-asc

Order. attention (default) puts students who need a nudge first; recent/oldest by join date; progress by completed lessons; activity by last activity.

emailstring

Search by email or name within the course.

starting_afterstring

Cursor received in next_starting_after. Omit on the first page.

limitinteger 1..200

Maximum students per page: 1–200; default 50.

bash
curl "https://www.clonify.com/api/v1/products/1/students" \
  -H "Authorization: Bearer clk_live_…"
json
{
  "object": "list",
  "data": [
    {
      "object": "course_student",
      "visitor_id": 88,
      "contact_id": 135,
      "email": "student@example.test",
      "name": "Student",
      "status": "active",
      "status_days": 1,
      "needs_attention": false,
      "last_activity_at": "2026-10-13T21:36:00Z",
      "last_activity": {
        "kind": "video",
        "at": "2026-10-13T21:36:00Z",
        "lesson_id": 1004,
        "lesson_title": "Lesson 4",
        "live_id": null,
        "live_title": null
      },
      "last_marked_at": "2026-09-22T10:00:00Z",
      "completed_lessons": 3,
      "total_lessons": 12,
      "completed_lesson_ids": [
        1001,
        1002,
        1003
      ],
      "started_lesson_ids": [
        1004
      ],
      "access": {
        "source": "purchase",
        "by_tag": true,
        "included_from_product_ids": [],
        "granted_at": "2026-08-30T10:00:00Z",
        "expires_at": null,
        "paused": false
      }
    }
  ],
  "has_more": true,
  "next_starting_after": "bzo1MA",
  "summary": {
    "total": 14,
    "matching": 14,
    "by_status": {
      "active": 5,
      "at_risk": 3,
      "stalled": 3,
      "not_started": 2,
      "completed": 1
    },
    "needs_attention": 7,
    "at_risk_after_days": 7,
    "stalled_after_days": 14
  },
  "measurement": {
    "activity": "Any study signal in this course: completing a lesson, opening it, watching its video, attending one of the course's live sessions or watching its recording.",
    "coverage": "Lesson opens and video watching are recorded since 2026-09-24 and only for campus and Bunny players (not YouTube, Vimeo or other sites); earlier, only completed lessons exist.",
    "paging": "Pages follow the requested order at request time; if students change between calls a page may repeat or skip someone. Re-read all pages to refresh."
  }
}
GET/api/v1/learning/playback

Read video playback evidence

New observations from supported course, broadcast and replay players (Bunny and Mux). Requires exactly product_id for lessons or webinar_id for broadcasts/replays. Rows are increments, not totals: visible_playing_seconds estimates playback with a visible tab; media_seconds is played media advancement, adjusted for speed when available. Repeated content counts again, so this is not unique coverage. Seeking does not credit skipped content. pause_count counts pauses; seek_count counts observed seeks/discontinuities; position_seconds is a position, not watch time. Browser-reported data capped by server time; not proof of attention or fully tamper-proof. visitor_id represents a verified session; registrant_id without a visitor represents a shareable registration link. Those identity types are not automatically merged. Pauses, hidden tabs and long gaps are excluded; closing or losing connectivity can lose the tail. Iframes without verified playback speed use conservative 1x measurement and can undercount or report discontinuities at other speeds. Excludes audio and other external providers without an adapter. Submission permissions last up to 12 hours after authorized access and do not grant video access. resource identifies the observed content. No historical reconstruction, completion changes or FUNDAE certification. Cursor exports new samples; date and visitor filters are unsupported, filter returned fields in the client.

Query parameters

product_idinteger

Course identifier. Supply this or webinar_id, never both.

webinar_idinteger

Webinar whose broadcast or replay is requested. Never automatically attributed to a course.

starting_afterinteger

Last received sample ID to continue export.

limitinteger 1..200

Maximum samples: 1–200; default 100.

bash
curl "https://www.clonify.com/api/v1/learning/playback" \
  -H "Authorization: Bearer clk_live_…"
json
{
  "object": "list",
  "data": [
    {
      "id": "1",
      "session_id": "6e2c91cc-15a5-4c46-a5cd-1db7703c1d20",
      "visitor_id": 88,
      "registrant_id": null,
      "kind": "course",
      "product_id": 100,
      "unit_id": 1000,
      "block_id": 1001,
      "webinar_id": null,
      "resource": "content-fingerprint",
      "occurred_at": "2026-09-24T12:00:30Z",
      "visible_playing_seconds": 30,
      "media_seconds": 60,
      "position_seconds": 90,
      "pause_count": 0,
      "seek_count": 1,
      "closed": false
    }
  ],
  "has_more": false,
  "next_starting_after": null,
  "measurement": {
    "source": "visible_player_client_reported",
    "limits": "Not proof of attention. Media seconds include repeats, not unique coverage. Missing telemetry is not reconstructed. Server caps concurrent time per known visitor or registration."
  }
}
GET/api/v1/learning/attendance

Read group session connections

New evidence from activation, confirmed by signed LiveKit callbacks. One row per registration with connections to the main room and breakout rooms. confirmed_connected_seconds unions complete intervals without double-counting overlapping devices or rooms for the same registration. entry_only and exit_only have unknown boundaries and contribute no duration; closed has observed entry and exit. Excludes broadcasts, page visits and the waiting room. identity_source is registration_link or verified_session; visitor_id requires a verified session with matching email and academy. A forwarded link cannot prove the human identity. audience_product_ids snapshots the configured audience, not enrollment or course time allocation. Does not prove attention or FUNDAE compliance. No historical reconstruction or guaranteed document retention. Date and product_id filters are unsupported: for attendance across every session of a course between two dates, with these seconds already summed, use GET /api/v1/webinars/attendance. Re-read every page to refresh connections; the cursor does not order changes.

Query parameters

webinar_idintegerrequired

Required group session identifier.

visitor_idinteger

Filter registrations linked to this verified learner or their same-email identities within the academy. Each connection retains its own identity source.

starting_afterinteger

Registration ID returned in next_starting_after. Omit on the first page.

limitinteger 1..100

Maximum registrations: 1–100; default 50.

bash
curl "https://www.clonify.com/api/v1/learning/attendance" \
  -H "Authorization: Bearer clk_live_…"
json
{
  "object": "list",
  "data": [
    {
      "id": "10",
      "registrant_id": 10,
      "registrant_email": "student@example.test",
      "registrant_name": "Student",
      "webinar_id": 50,
      "confirmed_connected_seconds": 120,
      "has_incomplete_connections": false,
      "connections": [
        {
          "room_name": "webinar-example1",
          "room_sid": "RM_example",
          "participant_sid": "PA_example",
          "visitor_id": 88,
          "identity_source": "verified_session",
          "audience_product_ids": [
            100
          ],
          "joined_at": "2026-09-24T12:00:00Z",
          "left_at": "2026-09-24T12:02:00Z",
          "connected_seconds": 120,
          "join_event_id": "event-join",
          "leave_event_id": "event-leave",
          "status": "closed"
        }
      ]
    }
  ],
  "has_more": false,
  "next_starting_after": null,
  "measurement": {
    "source": "signed_livekit_webhooks",
    "scope": "group_sessions_only",
    "duration": "union of observed closed intervals per registration; incomplete intervals excluded",
    "limitations": "connection evidence, not attention or learning; forwarded links cannot prove the human identity",
    "refresh": "re-read all pages for the webinar; registrant IDs are not a changes cursor"
  }
}
GET/api/v1/learning/events

List learning evidence

New history from activation: lesson_opened, lesson_completed, lesson_uncompleted, quiz_submitted and assignment_submitted. Also exam_started, exam_submitted and exam_reviewed for standalone exams: submissions and reviews retain questions, answers and grades; review_status distinguishes grades awaiting review. certificate_issued retains each issuance and the checked requirements; certificate_pdf_generated identifies the generated PDF by SHA-256 without confirming receipt. Query exams by their own product_id; they are not automatically linked to a course. Does not reconstruct earlier activity, retain the files themselves or certify FUNDAE compliance. For lesson quizzes, source distinguishes server_graded from legacy_client_reported.

Query parameters

product_idintegerrequired

Course or internal exam product to query. Required; standalone exams use their own identifier.

visitor_idinteger

Student; includes identities sharing the same email within the academy.

starting_afterinteger

Pagination ID from next_starting_after. Omit on the first page.

limitinteger 1..200

Maximum records: 1–200; default 100.

fromISO 8601

Inclusive timestamp with timezone. Sessions filter by start time; their seconds are not clipped.

untilISO 8601

Exclusive timestamp with timezone. Sessions filter by start time; their seconds are not clipped.

bash
curl "https://www.clonify.com/api/v1/learning/events" \
  -H "Authorization: Bearer clk_live_…"
json
{
  "object": "list",
  "data": [
    {
      "id": "1",
      "visitor_id": 88,
      "visitor_email": "student@example.test",
      "product_id": 100,
      "unit_id": null,
      "block_id": null,
      "kind": "certificate_pdf_generated",
      "occurred_at": "2026-09-24T12:00:00Z",
      "evidence": {
        "source": "pdf_server",
        "verification_code": "example-code",
        "issued_at": "2026-09-24T11:00:00Z",
        "sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
        "bytes": 24000,
        "receipt_confirmed": false
      }
    }
  ],
  "has_more": false,
  "next_starting_after": null
}
GET/api/v1/learning/sessions

List lesson sessions

Estimated time with a visible lesson and active time with focus and recent interaction. Neither video watch time nor proof of learning. Hidden tabs do not accrue time; totals across devices are capped. States: open, closed and timed_out. Rows may change: re-fetch the complete period to refresh sessions; the cursor orders creation, not updates.

Query parameters

product_idintegerrequired

Required course ID.

visitor_idinteger

Student; includes identities sharing the same email within the academy.

starting_afterinteger

Pagination ID from next_starting_after. Omit on the first page.

limitinteger 1..200

Maximum records: 1–200; default 100.

fromISO 8601

Inclusive timestamp with timezone. Sessions filter by start time; their seconds are not clipped.

untilISO 8601

Exclusive timestamp with timezone. Sessions filter by start time; their seconds are not clipped.

bash
curl "https://www.clonify.com/api/v1/learning/sessions" \
  -H "Authorization: Bearer clk_live_…"
json
{
  "object": "list",
  "data": [
    {
      "id": "1",
      "session_id": "6e2c91cc-15a5-4c46-a5cd-1db7703c1d20",
      "visitor_id": 88,
      "visitor_email": "student@example.test",
      "product_id": 100,
      "unit_id": 1000,
      "started_at": "2026-09-23T12:00:00Z",
      "last_seen_at": "2026-09-23T12:01:00Z",
      "ended_at": null,
      "connected_seconds": 60,
      "active_seconds": 30,
      "status": "open"
    }
  ],
  "has_more": false,
  "next_starting_after": null,
  "measurement": {
    "active_seconds": "visible activity estimate; not video watch time or proof of learning",
    "collection_started": "deployment; no historical reconstruction"
  }
}
GET/api/v1/products/{id}/feedback/responses

List a course's feedback responses

Students' answers to the course's feedback surveys: the one shown «In all lessons» (survey course) and those added to a single lesson (survey lesson). Same rows as the CSV export in the course's Feedback tab. Ordered by last answer, newest first. Each response carries the IDs of the student (visitor_id, plus contact_id when they have a Contacts record), lesson, module, survey and each question. When a student changes their answer, the same row changes (same id, new updated_at). Answers to questions no longer in the survey are not returned. A response that changes while you page moves to the top: if you had not received it yet, that pass misses it, and if it was the cursor, responses repeat. When you finish, request again with since set to the most recent updated_at you received to pick up those changes, and deduplicate by id. Returns 404 if the product is not a course in your account.

Path parameters

idintegerrequired

Course ID.

Query parameters

sinceISO 8601

Only responses changed after this timestamp (exclusive), with timezone. To fetch only new ones, pass the time of your previous sync.

untilISO 8601

Only responses changed up to this timestamp (inclusive), with timezone.

starting_afterinteger

ID from next_starting_after. Omit on the first page. An ID that is not a response of this course returns 400.

limitinteger 1..200

Responses per page: 1–200; default 50.

bash
curl "https://www.clonify.com/api/v1/products/1/feedback/responses" \
  -H "Authorization: Bearer clk_live_…"
json
{
  "object": "list",
  "data": [
    {
      "object": "feedback_response",
      "id": 31,
      "product_id": 420,
      "survey": "course",
      "common_block_id": 19,
      "block_id": null,
      "unit_id": 71,
      "unit_title": "Lesson 1",
      "module_id": 10,
      "module_title": "Module A",
      "visitor_id": 88,
      "contact_id": 3,
      "visitor_email": "student@example.test",
      "visitor_name": "Student",
      "created_at": "2026-09-27T08:00:00.000Z",
      "updated_at": "2026-09-28T09:00:00.000Z",
      "answers": [
        {
          "question_id": "4b6f0d0e-5d1a-4f0e-9a52-2f3c1e7d9a10",
          "question": "How was this lesson?",
          "kind": "stars",
          "value": 5
        },
        {
          "question_id": "9c1d2e3f-0a4b-4c5d-8e6f-7a8b9c0d1e2f",
          "question": "What would you improve?",
          "kind": "text",
          "value": "More examples"
        }
      ]
    }
  ],
  "has_more": true,
  "next_starting_after": 31
}