# Students

> Provision students with a login email and license, map personal emails, suspend and reactivate.

Canonical: https://grout.app/developer/documentation/people/students/

## How student accounts work

Every institution student signs in with a **generated login email** of the form `studentN.<code>@grout.user`. One-time codes are delivered to the student's **personal email**, which is mapped on first login (or ahead of time by you). The personal email is never returned by the API.

Provisioning creates three things atomically: the `users` row, the institution email record, and a **student license** (365 days by default, 2 devices).

## Create a student

```bash
curl -X POST $API/students -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' -d '{
  "full_name": "Asha Rao",
  "student_id": "S-2026-0142",
  "personal_email": "asha@example.com",
  "license_days": 365
}'
```

```json
{ "success": true, "data": {
  "id": "u_…", "email": "student143.4f9a2b1c@grout.user", "login_email": "student143.4f9a2b1c@grout.user",
  "user_type": "institution_student", "full_name": "Asha Rao", "student_id": "S-2026-0142",
  "status": "pending", "personal_email_mapped": true,
  "license": { "id": "lic_…", "status": "active", "license_type": "student", "expires_at": "2027-09-14T…", "is_lifetime": false }
} }
```

| Field | Notes |
|---|---|
| `full_name` | Optional; shown in the apps. |
| `student_id` | Your SIS identifier; searchable via `?student_id=`. |
| `personal_email` | Optional. When set, first login goes straight to the OTP screen. |
| `license_days` | 1–3650, default 365. |

`status` is `pending` until the first successful sign-in, then `active`.

:::tip Store the login email
The response's `login_email` is what the student types on `/login`. Save it (and `id`) against the SIS record so you never provision twice.
:::

## Bulk create

`POST /students/bulk` takes `{ "students": [ …up to 200… ] }` and returns per-item results:

```json
{ "created": 198, "failed": 2, "results": [ { "index": 0, "id": "u_…", "login_email": "…" }, { "index": 7, "error": "…" } ] }
```

## List and find

```bash
curl "$API/students?status=active&limit=200&page=1" -H "Authorization: Bearer $KEY"
curl "$API/students?student_id=S-2026-0142" -H "Authorization: Bearer $KEY"
curl "$API/students?group_id=grp_…" -H "Authorization: Bearer $KEY"
```

`GET /students/{id}` accepts the internal id **or** the login email.

## Update, suspend, reactivate

```bash
curl -X PATCH $API/students/u_… -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
  -d '{ "full_name": "Asha K. Rao", "personal_email": "asha.new@example.com" }'
curl -X POST $API/students/u_…/suspend    -H "Authorization: Bearer $KEY"
curl -X POST $API/students/u_…/reactivate -H "Authorization: Bearer $KEY"
```

Suspending ends the student's sessions immediately; their license stays and resumes on reactivation.

## Related reads

- `GET /students/{id}/exams` — assigned exams with submission and attempt state.
- `GET /students/{id}/guardians` — linked guardians. See [Guardians](/developer/documentation/people/guardians/).

## Events

`student.created`, `student.email_mapped`, `student.updated`, `license.generated`. See [Event Catalogue](/developer/documentation/webhooks/events/).
