Skip to content
cursareDocs
Sign inGet started
Documentation
  • Overview
  • Index2
  • Organization17
  • Sales6
  • Insights8
  • Reports and safety7
  • Minor accounts8
  • Minor safety3
  • Offers11
  • Media1
  • Contents10
  • Learner delivery1
  • Learner runtime5
  • Learners12
  • Cohorts12
  • Intake forms5
  • Campaigns4
  • Teams8
  • Affiliates7
  • Links7
  • Reviews1
  • Integrations26
  • MCP2
  • Webhooks6

Learners

Enrollments, approval requests and per-learner progress.

12 endpoints

List all enrollments

GET/learners

Enrollments visible to the caller, paginated. Owner/admin sees the whole organization; other members see only courses they own or whose owning team grants them learners.

Query parameters

pageinteger
pageSizeinteger
qstring

Search by learner name/email.

statusenum

Filter by completion state.

in-progresscompleted

Responses

200List all enrollments.
400Validation failed or the request body is malformed.
401Missing, invalid or revoked API key.
429Rate limit exceeded (240 reads/min, 60 writes/min per key).
500Unexpected server error.
curl https://api.cursare.com/v1/learners \
  --header 'Authorization: Bearer cr_live_…'
{
  "data": {
    "learners": [
      {
        "accessEndsAt": "2026-01-31T12:00:00.000Z",
        "accessStartsAt": "2026-01-31T12:00:00.000Z",
        "completed": true,
        "completedAt": "2026-01-31T12:00:00.000Z",
        "contentId": "string",
        "contentSlug": "string",
        "doneSteps": 0,
        "email": "learner@example.com",
        "enrolledAt": "2026-01-31T12:00:00.000Z",
        "id": "string",
        "name": "string",
        "totalSteps": 0,
        "userId": "string"
      }
    ],
    "page": 0,
    "pageSize": 0,
    "total": 0
  }
}

List a content's learners

GET/contents/{id}/learners

The content's roster with per-learner progress, enrollment status, cohort, and intake answers. Requires ownership, org owner/admin, or the learners role on the owning team.

Path parameters

idstring

The content id.

Responses

200List a content's learners.
401Missing, invalid or revoked API key.
403The key's organization does not own this resource.
404The resource does not exist (or belongs to another org).
429Rate limit exceeded (240 reads/min, 60 writes/min per key).
500Unexpected server error.
curl https://api.cursare.com/v1/contents/{id}/learners \
  --header 'Authorization: Bearer cr_live_…'
{
  "data": [
    {
      "accessEndsAt": "2026-01-31T12:00:00.000Z",
      "accessStartsAt": "2026-01-31T12:00:00.000Z",
      "cohortId": "string",
      "completed": true,
      "doneSteps": 0,
      "email": "learner@example.com",
      "enrolledAt": "2026-01-31T12:00:00.000Z",
      "intakeAnswers": {},
      "lastQuiz": {
        "score": 0,
        "total": 0
      },
      "name": "string",
      "status": "active",
      "totalSteps": 0,
      "userId": "string"
    }
  ]
}

Enroll a learner by email

POST/contents/{id}/learners

Admin grant — no purchase required; the seat cap still applies. The email must belong to an existing Cursare user. On a cohort-delivered offer the learner is bound to a dated cohort: pass cohortId to pick one, or omit it to follow the course's cohort policy (auto-open, or a 400 when no cohort is open).

Path parameters

idstring

The content id.

Request body

emailemailrequired
cohortIdstring

Delivery courses only: the dated cohort (cohort) to enroll the learner into. Ignored for self-paced offers.

Responses

201Enroll a learner by email.
400Validation failed or the request body is malformed.
401Missing, invalid or revoked API key.
403The key's organization does not own this resource.
404The resource does not exist (or belongs to another org).
429Rate limit exceeded (240 reads/min, 60 writes/min per key).
500Unexpected server error.
curl https://api.cursare.com/v1/contents/{id}/learners \
  --request POST \
  --header 'Authorization: Bearer cr_live_…' \
  --header 'Content-Type: application/json' \
  --data '{
  "email": "learner@example.com",
  "cohortId": "string"
}'
{
  "data": {
    "accessEndsAt": "2026-01-31T12:00:00.000Z",
    "accessStartsAt": "2026-01-31T12:00:00.000Z",
    "email": "learner@example.com",
    "enrolledAt": "2026-01-31T12:00:00.000Z",
    "userId": "string"
  }
}

Revoke an enrollment

DELETE/contents/{id}/learners/{userId}

Revokes the root enrollment and its contextual curriculum access without deleting the learner's progress or history. Re-enrolling the learner restores access to the same root-scoped progress. Nested courses never receive separate enrollment rows.

Path parameters

idstring

The content id.

userIdstring

The Cursare user id.

Responses

200Revoke an enrollment.
401Missing, invalid or revoked API key.
403The key's organization does not own this resource.
404The resource does not exist (or belongs to another org).
429Rate limit exceeded (240 reads/min, 60 writes/min per key).
500Unexpected server error.
curl https://api.cursare.com/v1/contents/{id}/learners/{userId} \
  --request DELETE \
  --header 'Authorization: Bearer cr_live_…'
{
  "data": {
    "contentId": "string",
    "unenrolled": true,
    "userId": "string"
  }
}

Set a learner's internal metadata

PATCH/contents/{id}/learners/{userId}

Replaces the organization's private metadata on ONE enrollment — a Stripe-style key→value bag for your own systems (CRM id, cohort, lead source, internal notes). It is REPLACE, not merge: the object you send becomes the whole bag, so send every key you want to keep (an empty object clears it). Never shown to the learner. Keys and values are strings. The response returns the resulting bag. Org-admin only (owner/admin) — metadata is not exposed on the viewer-scoped roster read.

Path parameters

idstring

The content id.

userIdstring

The Cursare user id of the enrolled learner.

Request body

metadataobjectrequired

Responses

200Set a learner's internal metadata.
400Validation failed or the request body is malformed.
401Missing, invalid or revoked API key.
403The key's organization does not own this resource.
404The resource does not exist (or belongs to another org).
429Rate limit exceeded (240 reads/min, 60 writes/min per key).
500Unexpected server error.
curl https://api.cursare.com/v1/contents/{id}/learners/{userId} \
  --request PATCH \
  --header 'Authorization: Bearer cr_live_…' \
  --header 'Content-Type: application/json' \
  --data '{
  "metadata": {}
}'
{
  "data": {
    "contentId": "string",
    "metadata": {},
    "userId": "string"
  }
}

Reset a learner's progress

POST/contents/{id}/learners/{userId}/reset

Restarts the course for ONE learner. DESTRUCTIVE — their progress is ERASED (every section they had completed, on the course and on its modules) and their completion is reopened; there is no undo. The enrollment SURVIVES: they keep access and their seat, and no purchase is refunded — this restarts the course, it does not remove the learner (that is unenrollLearner). Quiz history is kept, so mastery and insights survive. Drip re-anchors, because the per-section completion timestamps that pace it are wiped. A user id that isn't enrolled is a silent no-op. Confirm with a human before calling this. Use resetLearnerProgressBulk to restart several learners in one request.

Path parameters

idstring

The content id.

userIdstring

The Cursare user id.

Responses

200Reset a learner's progress.
401Missing, invalid or revoked API key.
403The key's organization does not own this resource.
404The resource does not exist (or belongs to another org).
429Rate limit exceeded (240 reads/min, 60 writes/min per key).
500Unexpected server error.
curl https://api.cursare.com/v1/contents/{id}/learners/{userId}/reset \
  --request POST \
  --header 'Authorization: Bearer cr_live_…'
{
  "data": {
    "contentId": "string",
    "reset": true,
    "userId": "string"
  }
}

Reset many learners' progress

POST/contents/{id}/learners/reset

The bulk sibling of resetLearnerProgress: restarts the course for every listed learner in one shot. DESTRUCTIVE — each learner's progress is ERASED (every section they had completed, on the course and on its modules) and their completion is reopened; there is no undo. The enrollments SURVIVE: everyone keeps access and their seat, and no purchase is refunded — this restarts the course, it does not remove learners (that is unenrollLearner). Quiz history is kept, so mastery and insights survive. Drip re-anchors, because the per-section completion timestamps that pace it are wiped. Ids that aren't enrolled are silently skipped, so compare reset against the number you sent. Confirm with a human before calling this on a real roster.

Path parameters

idstring

The content id.

Request body

userIdsstring[]required

Responses

200Reset many learners' progress.
400Validation failed or the request body is malformed.
401Missing, invalid or revoked API key.
403The key's organization does not own this resource.
404The resource does not exist (or belongs to another org).
429Rate limit exceeded (240 reads/min, 60 writes/min per key).
500Unexpected server error.
curl https://api.cursare.com/v1/contents/{id}/learners/reset \
  --request POST \
  --header 'Authorization: Bearer cr_live_…' \
  --header 'Content-Type: application/json' \
  --data '{
  "userIds": [
    "string"
  ]
}'
{
  "data": {
    "contentId": "string",
    "requested": 0,
    "reset": 0
  }
}

Get a learner's mastery signal

GET/contents/{id}/learners/{userId}/mastery

The adaptive-learning read for ONE learner on ONE course: which questions they keep missing and which modules they should revisit. Past attempts are re-graded against the CURRENT published answer key, so questions since removed simply drop out. Use it to answer 'what is this learner weak on?' and to decide what to review. Read-only, and it never exposes the answer key — you get counts per question id, not the correct options. The user must be enrolled in the content.

Path parameters

idstring

The content id.

userIdstring

The Cursare user id of the enrolled learner.

Responses

200Get a learner's mastery signal.
401Missing, invalid or revoked API key.
403The key's organization does not own this resource.
404The resource does not exist (or belongs to another org).
429Rate limit exceeded (240 reads/min, 60 writes/min per key).
500Unexpected server error.
curl https://api.cursare.com/v1/contents/{id}/learners/{userId}/mastery \
  --header 'Authorization: Bearer cr_live_…'
{
  "data": {
    "contentId": "string",
    "lastQuiz": {
      "score": 0,
      "total": 0
    },
    "questions": [
      {
        "correct": 0,
        "misses": 0,
        "needsWork": true,
        "questionId": "string"
      }
    ],
    "reviewHints": [
      {
        "contentId": "string",
        "routeSegment": "string",
        "score": 0,
        "title": "string",
        "total": 0
      }
    ],
    "userId": "string"
  }
}

List pending enrollment requests

GET/contents/{id}/enrollment-requests

The content's approval queue, oldest first, with each learner's intake answers. Paid requests expose their exact 30-day expiresAt; free requests return null. When refundPending is true, do not attempt a manual decision — the daily refund job owns it.

Path parameters

idstring

The content id.

Responses

200List pending enrollment requests.
401Missing, invalid or revoked API key.
403The key's organization does not own this resource.
404The resource does not exist (or belongs to another org).
429Rate limit exceeded (240 reads/min, 60 writes/min per key).
500Unexpected server error.
curl https://api.cursare.com/v1/contents/{id}/enrollment-requests \
  --header 'Authorization: Bearer cr_live_…'
{
  "data": [
    {
      "answers": {},
      "email": "learner@example.com",
      "expiresAt": "2026-01-31T12:00:00.000Z",
      "id": "string",
      "name": "string",
      "refundPending": true,
      "requestedAt": "2026-01-31T12:00:00.000Z",
      "status": "pending",
      "userId": "string"
    }
  ]
}

Count pending enrollment requests per course

GET/enrollment-requests/summary

The approvals inbox at a glance, org-wide: how many people are waiting on staff approval, grouped by course and ordered busiest first. Cheap enough to poll — call it to say 'you have N people waiting across M courses', then drill into a specific course with listEnrollmentRequests to see who they are and approve or reject them. Owner/admin sees the whole organization; other members see only courses they own or whose owning team grants them the learners role.

Responses

200Count pending enrollment requests per course.
401Missing, invalid or revoked API key.
429Rate limit exceeded (240 reads/min, 60 writes/min per key).
500Unexpected server error.
curl https://api.cursare.com/v1/enrollment-requests/summary \
  --header 'Authorization: Bearer cr_live_…'
{
  "data": {
    "contents": [
      {
        "contentId": "string",
        "pending": 0,
        "slug": "string"
      }
    ],
    "pending": 0
  }
}

Approve an enrollment request

POST/enrollment-requests/{id}/approve

Enrolls the learner (the seat cap still applies) and records the request as approved. The intake answers collected with the request travel onto the enrollment. A paid request cannot be approved at or after its 30-day expiry; it is waiting for automatic refund.

Path parameters

idstring

The enrollment request id.

Responses

200Approve an enrollment request.
400Validation failed or the request body is malformed.
401Missing, invalid or revoked API key.
403The key's organization does not own this resource.
404The resource does not exist (or belongs to another org).
429Rate limit exceeded (240 reads/min, 60 writes/min per key).
500Unexpected server error.
curl https://api.cursare.com/v1/enrollment-requests/{id}/approve \
  --request POST \
  --header 'Authorization: Bearer cr_live_…'
{
  "data": {
    "approved": true
  }
}

Reject an enrollment request

POST/enrollment-requests/{id}/reject

Records the request as rejected — the learner may ask again later. A paid, unrefunded purchase is refunded automatically; if that refund fails the rejection still stands, refundIssued is false, and the daily worker retries it. An optional reason is emailed to the learner best-effort. Send {} to reject without a note. An expired paid request cannot be rejected manually because its row is the durable retry queue for the daily refund.

Path parameters

idstring

The enrollment request id.

Request body

reasonstring

Responses

200Reject an enrollment request.
400Validation failed or the request body is malformed.
401Missing, invalid or revoked API key.
403The key's organization does not own this resource.
404The resource does not exist (or belongs to another org).
429Rate limit exceeded (240 reads/min, 60 writes/min per key).
500Unexpected server error.
curl https://api.cursare.com/v1/enrollment-requests/{id}/reject \
  --request POST \
  --header 'Authorization: Bearer cr_live_…' \
  --header 'Content-Type: application/json' \
  --data '{
  "reason": "string"
}'
{
  "data": {
    "refundIssued": true,
    "rejected": true
  }
}

Your privacy choices

The technical Tag Manager container is required to apply your choices. Analytics and marketing cookies and storage remain optional and stay off until you authorize them. Privacy Policy.

Sign-in, security, core functions and consent enforcement.

Audience measurement such as Google Analytics.

Marketing storage, campaign attribution and optional tags.