API reference

Download PDF

Every endpoint a key can call, with its parameters, response fields and a ready-to-run example.

Read your school's data from your own tools.

Authenticating

Every request carries an API key as a bearer token:

Authorization: Bearer YOUR_API_KEY

Create a key at Settings → API keys. The full key is shown once, when you create it. Keys are read-only, reach only your own school, and stop working the moment you revoke them.

Start with GET /api/reporting/v1/health — it proves the key and the header are right before you touch any data.

Conventions

  • Field names are snake_case; timestamps are ISO-8601 in UTC; money is always integer cents, never a decimal.
  • List endpoints return { data, total, page, page_size }. total counts every matching row, not just the page.
  • Paginate with ?page= and ?page_size=. page_size is clamped to 1–100, and defaults to 50.
  • Errors return { "error": { "code", "message" } } with a matching HTTP status. Branch on code — it is stable; message may be reworded.

Rate limits

Requests are limited per key: 600 requests per 60 seconds. Over the limit you get 429 with code rate_limited. Give each integration its own key so one busy tool cannot exhaust another's budget.

Language

Core data

GET/api/public/v1/contacts

List contacts

Newest first. Combine filters to narrow the list; omitted filters are not applied.

Parameters
NameInTypeDescription
pagequeryintegerPage number, starting at 1. Values below 1 are clamped to 1.
page_sizequeryintegerRows per page. Clamped to the 1–100 range.
rolequerystringOnly contacts with this role.
statusquerystringOnly contacts with this `contact_status`, e.g. `active`.
marketing_statusquerystringOnly contacts with this email consent state, e.g. `subscribed`.
Example
curl -X GET 'https://app.zinstruct.com/api/public/v1/contacts?page=1&page_size=50' \
  -H 'Authorization: Bearer YOUR_API_KEY'
Response fields — Contact

A person in your CRM — a lead, a member, or a customer.

FieldTypeDescription
idstring (uuid)Stable identifier.
emailstringEmail address.
namestring | nullDisplay name. Falls back to first + last name when the name field is empty.
first_namestring | nullGiven name.
last_namestring | nullFamily name.
rolestring | nullYour own classification of this contact.
phonestring | nullPhone number. Falls back to the mobile number when the primary is empty.
contact_statusstringLifecycle state, e.g. `active` or `archived`. Defaults to `active`.
marketing_statusstringEmail consent, e.g. `subscribed` or `unsubscribed`. Defaults to `unsubscribed`.
sourcestring | nullWhere the contact first came from. Immutable first-touch provenance.
lifetime_value_centsintegerTotal revenue attributed to this contact, in cents.
created_atstring | nullWhen the contact was created. ISO-8601, UTC.
last_activity_atstring | nullMost recent recorded activity. ISO-8601, UTC.
Responses
  • 200 — A page of contacts.
  • 401 — The token is missing, malformed, invalid, or revoked.
  • 429 — Rate limit exceeded for this key.
GET/api/public/v1/contacts/{id}

Get one contact

Parameters
NameInTypeDescription
id*pathstring (uuid)The contact id.
Example
curl -X GET 'https://app.zinstruct.com/api/public/v1/contacts/00000000-0000-0000-0000-000000000000' \
  -H 'Authorization: Bearer YOUR_API_KEY'
Response fields — Contact

A person in your CRM — a lead, a member, or a customer.

FieldTypeDescription
idstring (uuid)Stable identifier.
emailstringEmail address.
namestring | nullDisplay name. Falls back to first + last name when the name field is empty.
first_namestring | nullGiven name.
last_namestring | nullFamily name.
rolestring | nullYour own classification of this contact.
phonestring | nullPhone number. Falls back to the mobile number when the primary is empty.
contact_statusstringLifecycle state, e.g. `active` or `archived`. Defaults to `active`.
marketing_statusstringEmail consent, e.g. `subscribed` or `unsubscribed`. Defaults to `unsubscribed`.
sourcestring | nullWhere the contact first came from. Immutable first-touch provenance.
lifetime_value_centsintegerTotal revenue attributed to this contact, in cents.
created_atstring | nullWhen the contact was created. ISO-8601, UTC.
last_activity_atstring | nullMost recent recorded activity. ISO-8601, UTC.
Responses
  • 200 — The contact.
  • 401 — The token is missing, malformed, invalid, or revoked.
  • 404 — No contact with that id.
  • 429 — Rate limit exceeded for this key.
GET/api/public/v1/products

List products

Newest first.

Parameters
NameInTypeDescription
pagequeryintegerPage number, starting at 1. Values below 1 are clamped to 1.
page_sizequeryintegerRows per page. Clamped to the 1–100 range.
typequerystringOnly products of this type, e.g. `course`.
statusquerystringOnly products with this status, e.g. `published`.
Example
curl -X GET 'https://app.zinstruct.com/api/public/v1/products?page=1&page_size=50' \
  -H 'Authorization: Bearer YOUR_API_KEY'
Response fields — Product

A catalog item — a course, coaching program, community, download, newsletter, or podcast.

FieldTypeDescription
idstring (uuid)Stable identifier.
typestringProduct type, e.g. `course`, `coaching`, `community`, `download`, `newsletter`, `podcast`.
sub_typestring | nullFurther classification within the type, where one applies.
titlestringProduct title.
descriptionstring | nullProduct description.
statusstring`draft` or `published`. Defaults to `draft`.
created_atstring | nullWhen the product was created. ISO-8601, UTC.
updated_atstring | nullWhen the product was last changed. ISO-8601, UTC.
Responses
  • 200 — A page of products.
  • 401 — The token is missing, malformed, invalid, or revoked.
  • 429 — Rate limit exceeded for this key.
GET/api/public/v1/products/{id}

Get one product

Parameters
NameInTypeDescription
id*pathstring (uuid)The product id.
Example
curl -X GET 'https://app.zinstruct.com/api/public/v1/products/00000000-0000-0000-0000-000000000000' \
  -H 'Authorization: Bearer YOUR_API_KEY'
Response fields — Product

A catalog item — a course, coaching program, community, download, newsletter, or podcast.

FieldTypeDescription
idstring (uuid)Stable identifier.
typestringProduct type, e.g. `course`, `coaching`, `community`, `download`, `newsletter`, `podcast`.
sub_typestring | nullFurther classification within the type, where one applies.
titlestringProduct title.
descriptionstring | nullProduct description.
statusstring`draft` or `published`. Defaults to `draft`.
created_atstring | nullWhen the product was created. ISO-8601, UTC.
updated_atstring | nullWhen the product was last changed. ISO-8601, UTC.
Responses
  • 200 — The product.
  • 401 — The token is missing, malformed, invalid, or revoked.
  • 404 — No product with that id.
  • 429 — Rate limit exceeded for this key.
GET/api/public/v1/offers

List offers

Newest first.

Parameters
NameInTypeDescription
pagequeryintegerPage number, starting at 1. Values below 1 are clamped to 1.
page_sizequeryintegerRows per page. Clamped to the 1–100 range.
product_idquerystringOnly offers selling this product.
statusquerystringOnly offers with this status, e.g. `published`.
Example
curl -X GET 'https://app.zinstruct.com/api/public/v1/offers?page=1&page_size=50' \
  -H 'Authorization: Bearer YOUR_API_KEY'
Response fields — Offer

The sellable pricing of a product. One product can have several offers.

FieldTypeDescription
idstring (uuid)Stable identifier.
product_idstring (uuid)The product this offer sells.
namestringOffer name.
descriptionstring | nullOffer description.
price_centsintegerPrice in the smallest currency unit. Money is always integer cents — never a float.
currencystringISO-4217 currency code, lowercase. Defaults to `usd`.
planstring`one_time` or a recurring plan. Defaults to `one_time`.
intervalstring | nullBilling interval for recurring plans, e.g. `month` or `year`. Null for one-time offers.
interval_countintegerHow many intervals per billing cycle.
statusstring`draft` or `published`. Defaults to `draft`.
pwywbooleanTrue when the buyer names their own price.
trial_daysinteger | nullFree trial length in days, or null for no trial.
created_atstring | nullWhen the offer was created. ISO-8601, UTC.
updated_atstring | nullWhen the offer was last changed. ISO-8601, UTC.
Responses
  • 200 — A page of offers.
  • 401 — The token is missing, malformed, invalid, or revoked.
  • 429 — Rate limit exceeded for this key.
GET/api/public/v1/offers/{id}

Get one offer

Parameters
NameInTypeDescription
id*pathstring (uuid)The offer id.
Example
curl -X GET 'https://app.zinstruct.com/api/public/v1/offers/00000000-0000-0000-0000-000000000000' \
  -H 'Authorization: Bearer YOUR_API_KEY'
Response fields — Offer

The sellable pricing of a product. One product can have several offers.

FieldTypeDescription
idstring (uuid)Stable identifier.
product_idstring (uuid)The product this offer sells.
namestringOffer name.
descriptionstring | nullOffer description.
price_centsintegerPrice in the smallest currency unit. Money is always integer cents — never a float.
currencystringISO-4217 currency code, lowercase. Defaults to `usd`.
planstring`one_time` or a recurring plan. Defaults to `one_time`.
intervalstring | nullBilling interval for recurring plans, e.g. `month` or `year`. Null for one-time offers.
interval_countintegerHow many intervals per billing cycle.
statusstring`draft` or `published`. Defaults to `draft`.
pwywbooleanTrue when the buyer names their own price.
trial_daysinteger | nullFree trial length in days, or null for no trial.
created_atstring | nullWhen the offer was created. ISO-8601, UTC.
updated_atstring | nullWhen the offer was last changed. ISO-8601, UTC.
Responses
  • 200 — The offer.
  • 401 — The token is missing, malformed, invalid, or revoked.
  • 404 — No offer with that id.
  • 429 — Rate limit exceeded for this key.
GET/api/public/v1/enrollments

List enrollments

Most recently enrolled first.

Parameters
NameInTypeDescription
pagequeryintegerPage number, starting at 1. Values below 1 are clamped to 1.
page_sizequeryintegerRows per page. Clamped to the 1–100 range.
product_idquerystringOnly enrollments in this product.
contact_idquerystringOnly enrollments belonging to this contact.
statusquerystringOnly enrollments with this status, e.g. `active`.
Example
curl -X GET 'https://app.zinstruct.com/api/public/v1/enrollments?page=1&page_size=50' \
  -H 'Authorization: Bearer YOUR_API_KEY'
Response fields — Enrollment

A contact's access to a product, with cached progress.

FieldTypeDescription
idstring (uuid)Stable identifier.
contact_idstring (uuid)The enrolled contact.
product_idstring (uuid)The product they have access to.
granted_via_offer_idstring | nullThe offer that granted access, when access came from a purchase or claim.
statusstringAccess state, e.g. `active`. Defaults to `active`.
progress_pctnumberCompletion percentage, 0–100.
enrolled_atstring | nullWhen access began. ISO-8601, UTC.
completed_atstring | nullWhen the product was completed, or null. ISO-8601, UTC.
Responses
  • 200 — A page of enrollments.
  • 401 — The token is missing, malformed, invalid, or revoked.
  • 429 — Rate limit exceeded for this key.
GET/api/public/v1/enrollments/{id}

Get one enrollment

Parameters
NameInTypeDescription
id*pathstring (uuid)The enrollment id.
Example
curl -X GET 'https://app.zinstruct.com/api/public/v1/enrollments/00000000-0000-0000-0000-000000000000' \
  -H 'Authorization: Bearer YOUR_API_KEY'
Response fields — Enrollment

A contact's access to a product, with cached progress.

FieldTypeDescription
idstring (uuid)Stable identifier.
contact_idstring (uuid)The enrolled contact.
product_idstring (uuid)The product they have access to.
granted_via_offer_idstring | nullThe offer that granted access, when access came from a purchase or claim.
statusstringAccess state, e.g. `active`. Defaults to `active`.
progress_pctnumberCompletion percentage, 0–100.
enrolled_atstring | nullWhen access began. ISO-8601, UTC.
completed_atstring | nullWhen the product was completed, or null. ISO-8601, UTC.
Responses
  • 200 — The enrollment.
  • 401 — The token is missing, malformed, invalid, or revoked.
  • 404 — No enrollment with that id.
  • 429 — Rate limit exceeded for this key.

Engagement

GET/api/reporting/v1/courses

List courses with engagement

Every course with its structure counts, enrollment count, watch activity, and mean completion. Watch metrics read a materialized table and stay at 0 until viewing is recorded.

Parameters
NameInTypeDescription
pagequeryintegerPage number, starting at 1. Values below 1 are clamped to 1.
page_sizequeryintegerRows per page. Clamped to the 1–100 range.
Example
curl -X GET 'https://app.zinstruct.com/api/reporting/v1/courses?page=1&page_size=50' \
  -H 'Authorization: Bearer YOUR_API_KEY'
Response fields — Course

Engagement rollup for one course.

FieldTypeDescription
idstring (uuid)The course product id.
titlestringCourse title.
modules_countintegerModules in the course.
lessons_countintegerLessons across all modules.
students_enrolledintegerContacts with an enrollment in this course.
lessons_watched_over_30sintegerLesson views exceeding 30 seconds of watch time.
distinct_students_watchedintegerDistinct students with any recorded watch time.
avg_completion_pctnumberMean enrollment progress, 0–100.
Responses
  • 200 — A page of courses.
  • 401 — The token is missing, malformed, invalid, or revoked.
  • 429 — Rate limit exceeded for this key.
GET/api/reporting/v1/courses/{course_id}/modules

List a course’s modules with engagement

Modules in their display order, each with lesson count and watch activity.

Parameters
NameInTypeDescription
course_id*pathstring (uuid)The course product id.
pagequeryintegerPage number, starting at 1. Values below 1 are clamped to 1.
page_sizequeryintegerRows per page. Clamped to the 1–100 range.
Example
curl -X GET 'https://app.zinstruct.com/api/reporting/v1/courses/00000000-0000-0000-0000-000000000000/modules?page=1&page_size=50' \
  -H 'Authorization: Bearer YOUR_API_KEY'
Response fields — CourseModule

Engagement rollup for one module within a course.

FieldTypeDescription
idstring (uuid)The module id.
titlestringModule title.
lessons_countintegerLessons in this module.
lessons_watched_over_30sintegerLesson views exceeding 30 seconds of watch time.
distinct_students_watchedintegerDistinct students with any recorded watch time in this module.
Responses
  • 200 — A page of modules.
  • 401 — The token is missing, malformed, invalid, or revoked.
  • 404 — No course with that id.
  • 429 — Rate limit exceeded for this key.
GET/api/reporting/v1/students

List students with activity

Contacts with sign-in, session, and revenue metrics attached.

Parameters
NameInTypeDescription
pagequeryintegerPage number, starting at 1. Values below 1 are clamped to 1.
page_sizequeryintegerRows per page. Clamped to the 1–100 range.
rolequerystringOnly students with this role.
sortquerystringPass `last_active_at` to order by most recently active.
Example
curl -X GET 'https://app.zinstruct.com/api/reporting/v1/students?page=1&page_size=50' \
  -H 'Authorization: Bearer YOUR_API_KEY'
Response fields — Student

A contact viewed through the engagement lens: activity, sessions, and revenue.

FieldTypeDescription
idstring (uuid)The contact id. Same identifier the public API uses for contacts.
namestring | nullDisplay name.
emailstringEmail address.
rolestring | nullYour own classification of this contact.
joined_atstring | nullWhen the contact was created. ISO-8601, UTC.
last_active_atstring | nullMost recent recorded activity. ISO-8601, UTC.
logins_30dintegerSign-ins in the last 30 days.
avg_session_minutesnumberMean session length in minutes over the last 30 days, to one decimal place.
total_session_minutes_30dintegerTotal session minutes over the last 30 days.
payingbooleanTrue when this contact has ever bought.
lifetime_revenue_centsintegerTotal revenue attributed to this contact, in cents.
Responses
  • 200 — A page of students.
  • 401 — The token is missing, malformed, invalid, or revoked.
  • 429 — Rate limit exceeded for this key.
GET/api/reporting/v1/students/{student_id}

Get one student

Parameters
NameInTypeDescription
student_id*pathstring (uuid)The contact id.
Example
curl -X GET 'https://app.zinstruct.com/api/reporting/v1/students/00000000-0000-0000-0000-000000000000' \
  -H 'Authorization: Bearer YOUR_API_KEY'
Response fields — Student

A contact viewed through the engagement lens: activity, sessions, and revenue.

FieldTypeDescription
idstring (uuid)The contact id. Same identifier the public API uses for contacts.
namestring | nullDisplay name.
emailstringEmail address.
rolestring | nullYour own classification of this contact.
joined_atstring | nullWhen the contact was created. ISO-8601, UTC.
last_active_atstring | nullMost recent recorded activity. ISO-8601, UTC.
logins_30dintegerSign-ins in the last 30 days.
avg_session_minutesnumberMean session length in minutes over the last 30 days, to one decimal place.
total_session_minutes_30dintegerTotal session minutes over the last 30 days.
payingbooleanTrue when this contact has ever bought.
lifetime_revenue_centsintegerTotal revenue attributed to this contact, in cents.
Responses
  • 200 — The student.
  • 401 — The token is missing, malformed, invalid, or revoked.
  • 404 — No student with that id.
  • 429 — Rate limit exceeded for this key.
GET/api/reporting/v1/students/{student_id}/watch-history

List what a student has watched

Every lesson with recorded watch time for this student, newest first.

Parameters
NameInTypeDescription
student_id*pathstring (uuid)The contact id.
pagequeryintegerPage number, starting at 1. Values below 1 are clamped to 1.
page_sizequeryintegerRows per page. Clamped to the 1–100 range.
Example
curl -X GET 'https://app.zinstruct.com/api/reporting/v1/students/00000000-0000-0000-0000-000000000000/watch-history?page=1&page_size=50' \
  -H 'Authorization: Bearer YOUR_API_KEY'
Response fields — WatchHistoryEntry

One lesson a student has watched, with how much of it.

FieldTypeDescription
course_idstring | nullThe course the lesson belongs to.
course_titlestring | nullCourse title.
module_idstring | nullThe module the lesson belongs to.
module_titlestring | nullModule title.
lesson_idstring (uuid)The lesson id.
lesson_titlestringLesson title.
lesson_duration_secondsinteger | nullLesson length in seconds, when known.
watched_secondsintegerSeconds of this lesson the student has watched.
watched_pctnumberWatched seconds as a percentage of duration, 0–100.
watched_over_30sbooleanTrue when watch time exceeds 30 seconds.
last_watched_atstring | nullMost recent watch event for this lesson. ISO-8601, UTC.
Responses
  • 200 — A page of watch history.
  • 401 — The token is missing, malformed, invalid, or revoked.
  • 404 — No student with that id.
  • 429 — Rate limit exceeded for this key.
GET/api/reporting/v1/lessons

Rank lessons by earliest drop-off

Lessons ordered by how early their audience falls away — earliest drop first, larger drops breaking ties. Lessons with no retention data are excluded. This endpoint takes `limit` rather than `page_size`, and always reports page 1.

Parameters
NameInTypeDescription
sortquerystringOnly `worst_retention` is supported. Any other value returns 400.
limitqueryintegerHow many lessons to return. Clamped to the 1–100 range.
Example
curl -X GET 'https://app.zinstruct.com/api/reporting/v1/lessons?sort=worst_retention&limit=10' \
  -H 'Authorization: Bearer YOUR_API_KEY'
Response fields — WorstRetentionLesson

A lesson ranked by how early its audience falls away.

FieldTypeDescription
lesson_idstring (uuid)The lesson id.
titlestringLesson title.
biggest_dropoffDropoff
Responses
  • 200 — The ranked lessons.
  • 400 — An unsupported `sort` value.
  • 401 — The token is missing, malformed, invalid, or revoked.
  • 429 — Rate limit exceeded for this key.
GET/api/reporting/v1/lessons/{lesson_id}/retention

Get a lesson’s retention curve

The full drop-off curve for one lesson, plus the single biggest fall.

Parameters
NameInTypeDescription
lesson_id*pathstring (uuid)The lesson id.
Example
curl -X GET 'https://app.zinstruct.com/api/reporting/v1/lessons/00000000-0000-0000-0000-000000000000/retention' \
  -H 'Authorization: Bearer YOUR_API_KEY'
Response fields — LessonRetention

Video drop-off for one lesson — where viewers stop watching.

FieldTypeDescription
lesson_idstring (uuid)The lesson id.
titlestringLesson title.
course_idstring | nullThe course the lesson belongs to.
module_idstring | nullThe module the lesson belongs to.
duration_secondsinteger | nullLesson length in seconds, when known.
viewsintegerRecorded views.
avg_watch_secondsnumberMean seconds watched per view.
avg_watch_pctnumberMean percentage of the lesson watched, 0–100.
retentionRetentionPoint[]The curve, ordered by offset.
biggest_dropoffDropoff
Responses
  • 200 — The curve.
  • 401 — The token is missing, malformed, invalid, or revoked.
  • 404 — No lesson with that id.
  • 429 — Rate limit exceeded for this key.
GET/api/reporting/v1/summary

Get the KPI summary

A single flat object of headline numbers for your school — the cheapest call for a dashboard tile.

Example
curl -X GET 'https://app.zinstruct.com/api/reporting/v1/summary' \
  -H 'Authorization: Bearer YOUR_API_KEY'
Response fields — Summary

One flat object of pre-aggregated KPIs for your school.

FieldTypeDescription
tenantstringYour school slug.
studentsintegerTotal contacts.
active_students_30dintegerDistinct contacts with activity in the last 30 days.
paying_customersintegerContacts who have bought.
coursesintegerCourse products.
modulesintegerModules across all courses.
lessonsintegerLessons across all modules.
logins_30dintegerSign-ins in the last 30 days.
avg_session_minutesnumberMean session length in minutes.
lessons_watched_over_30s_30dintegerLesson views over 30 seconds in the last 30 days.
completion_rate_pctnumberCompleted enrollments as a percentage of all enrollments, 0–100.
Responses
  • 200 — The summary.
  • 401 — The token is missing, malformed, invalid, or revoked.
  • 429 — Rate limit exceeded for this key.
  • 500 — The summary could not be built.

Getting started

GET/api/reporting/v1/health

Check a key works

Returns `{ "ok": true }` when the key authenticated. The right first call when wiring up an integration: it proves the key and the header are correct without touching any data.

Example
curl -X GET 'https://app.zinstruct.com/api/reporting/v1/health' \
  -H 'Authorization: Bearer YOUR_API_KEY'
Response fields — Health

Liveness probe result.

FieldTypeDescription
okbooleanAlways true when the key authenticated.
Responses
  • 200 — The key is valid.
  • 401 — The token is missing, malformed, invalid, or revoked.
  • 429 — Rate limit exceeded for this key.