> ## Documentation Index
> Fetch the complete documentation index at: https://fayneos.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Enroll a Student in a Published Course via the API

> Grant a student access to a published course with a single API call. Re-enrolling a student who already has access restores their active status.

The enrollments endpoint lets you assign a course to a student. Assigning does two things at once: it records a course grant (the access itself, the same thing the dashboard's "Assign course" does) and creates an enrollment so the course appears in the student's My Learning immediately. You can call this endpoint multiple times for the same student and course, it is idempotent. Both the student and the course must exist in your academy, and the course must be published.

## Enroll a student

```text theme={null}
POST /api/v1/students/:studentId/enrollments
```

### Path parameters

<ParamField path="studentId" type="string" required>
  UUID of the student to enroll. The student must already be a member of your academy, add them first using [POST /api/v1/students](/docs/api-reference/students) if needed.
</ParamField>

### Body parameters

<ParamField body="course_id" type="string" required>
  UUID of the course to enroll the student in. The course must belong to your academy and be published. Draft courses return a `400` error.
</ParamField>

### Constraints

* The student identified by `studentId` must be an active member of your academy. A `404` is returned if they are not found.
* The course identified by `course_id` must exist in your academy and have a published status. A `404` is returned if it does not exist; a `400` is returned if it exists but is not published.

### Response fields

Returns the enrollment record with a `201` status code.

<ResponseField name="data" type="object" required>
  <Expandable title="properties" defaultOpen>
    <ResponseField name="id" type="string" required>
      UUID of the enrollment record. If re-enrolling, this is the ID of the existing record.
    </ResponseField>

    <ResponseField name="course_id" type="string" required>
      UUID of the course the student was enrolled in.
    </ResponseField>

    <ResponseField name="course_title" type="string" required>
      Display title of the course.
    </ResponseField>
  </Expandable>
</ResponseField>

### Example request

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST \
    https://yourname.fayneos.com/api/v1/students/c3d4e5f6-a7b8-9012-cdef-123456789012/enrollments \
    -H "Authorization: Bearer fa_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
    -H "Content-Type: application/json" \
    -d '{
      "course_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
    }'
  ```

  ```javascript JavaScript theme={null}
  const studentId = 'c3d4e5f6-a7b8-9012-cdef-123456789012';
  const response = await fetch(
    `https://yourname.fayneos.com/api/v1/students/${studentId}/enrollments`,
    {
      method: 'POST',
      headers: {
        'Authorization': 'Bearer fa_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        course_id: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'
      })
    }
  );
  const { data } = await response.json();
  ```
</CodeGroup>

### Example response

```json theme={null}
{
  "data": {
    "id": "e1f2a3b4-c5d6-7890-efab-123456789abc",
    "course_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "course_title": "Cold Outreach Mastery"
  }
}
```

### Error responses

```json theme={null}
{
  "error": {
    "code": "not_found",
    "message": "Student not found in this academy"
  }
}
```

```json theme={null}
{
  "error": {
    "code": "not_found",
    "message": "Course not found"
  }
}
```

```json theme={null}
{
  "error": {
    "code": "invalid_course",
    "message": "Only published courses can be assigned"
  }
}
```

***

## Revoke an enrollment

Revokes an assigned course: ends the student's course grant and marks the enrollment record as revoked (the record is preserved). Re-assigning later through [POST /api/v1/students/:studentId/enrollments](/docs/api-reference/enrollments) restores access on the same record.

Access in Fayne is layered. If the course is free for everyone, was purchased by the student, or is covered by an offer they hold, revoking the assignment does not lock the course. The response tells you whether the student can still open the course after the revoke, so your automation never has to guess.

```text theme={null}
DELETE /api/v1/students/:studentId/enrollments/:enrollmentId
```

### Path parameters

<ParamField path="studentId" type="string" required>
  UUID of the student whose enrollment you are revoking.
</ParamField>

<ParamField path="enrollmentId" type="string" required>
  UUID of the enrollment record to revoke. This is the `id` returned when the enrollment was created.
</ParamField>

### Response fields

<ResponseField name="data" type="object" required>
  <Expandable title="properties" defaultOpen>
    <ResponseField name="revoked" type="boolean" required>
      Always `true` when the operation succeeds.
    </ResponseField>

    <ResponseField name="still_has_access" type="boolean" required>
      Whether the student can still open the course after the revoke, computed live. `true` when the course is free for everyone, the student purchased it, or an offer they hold covers it.
    </ResponseField>

    <ResponseField name="still_has_access_via" type="string">
      Why access survives when `still_has_access` is `true`: `free`, `purchase`, or `offer`. `null` when the student lost access.
    </ResponseField>
  </Expandable>
</ResponseField>

### Example request

```bash theme={null}
curl -X DELETE \
  https://yourname.fayneos.com/api/v1/students/c3d4e5f6-a7b8-9012-cdef-123456789012/enrollments/e1f2a3b4-c5d6-7890-efab-123456789abc \
  -H "Authorization: Bearer fa_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
```

### Example response

```json theme={null}
{
  "data": {
    "revoked": true,
    "still_has_access": false,
    "still_has_access_via": null
  }
}
```

### Error responses

```json theme={null}
{
  "error": {
    "code": "not_found",
    "message": "Enrollment not found"
  }
}
```
