# SKILLS.md — Grout Institution API task recipes

Each skill is a self-contained task an agent (or a person) can perform with the Grout Institution API. Every skill lists the scopes it needs, the endpoints it touches, a request template, and whether it needs human confirmation. Served at https://grout.app/developer/SKILLS.md. Base URL `https://serverless.grout.app/v1`; `$KEY` is an institution API key.

Read https://grout.app/developer/AGENTS.md first for conventions and guardrails.

---

## verify-access
**Goal:** confirm the key works and which scopes it has before doing anything else.
**Scopes:** none. **Confirmation:** no.
```bash
curl -s $API/me -H "Authorization: Bearer $KEY"
```
Read `data.scopes` and `data.rate_limit_per_min`. Abort if a later skill needs a scope that is missing; tell the human which one.

## provision-student
**Goal:** create one student with a login email and license, optionally pre-mapping their personal email.
**Scopes:** `students:write`. **Confirmation:** no.
**Check first:** `GET /students?student_id=<sis id>` — if a row exists, use it.
```bash
curl -s -X POST $API/students -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
  -d '{"full_name":"<name>","student_id":"<sis id>","personal_email":"<email>","license_days":365}'
```
Persist `data.id` and `data.login_email`. Docs: /developer/documentation/people/students/

## provision-students-bulk
**Goal:** create up to 200 students per call.
**Scopes:** `students:write`. **Confirmation:** yes if more than 500 total (cost/licenses).
```bash
curl -s -X POST $API/students/bulk -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
  -d '{"students":[{"full_name":"…","student_id":"…","personal_email":"…"}, …]}'
```
Inspect `results[]`; items with `error` need attention. Docs: /developer/documentation/people/students/

## provision-faculty
**Goal:** create a teacher who can own exams and groups.
**Scopes:** `faculty:write`. **Confirmation:** no.
```bash
curl -s -X POST $API/faculty -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
  -d '{"full_name":"<name>","faculty_id":"<hr id>"}'
```
Docs: /developer/documentation/people/faculty/

## sync-section
**Goal:** mirror an LMS section as a Grout group with exact membership.
**Scopes:** `groups:write`, `students:read`. **Confirmation:** no.
1. `GET /groups?q=<section name>` → reuse `id` if found, else `POST /groups {name, faculty_id, student_ids}`.
2. `PATCH /groups/{id} {"student_ids":[…full list…]}` (replaces membership).
Docs: /developer/documentation/people/groups/ and /developer/documentation/guides/roster-sync/

## suspend-student
**Goal:** block sign-in for a student who left.
**Scopes:** `students:write`. **Confirmation:** **yes** (destructive to access).
```bash
curl -s -X POST $API/students/<id or login_email>/suspend -H "Authorization: Bearer $KEY"
```
Reverse with `/reactivate`.

## sso-launch-button
**Goal:** open GroutApp/GroutCode for a specific user from an LMS click.
**Scopes:** `sso:write`. **Confirmation:** no.
```bash
curl -s -X POST $API/sso/tokens -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
  -d '{"email":"<login_email>","app":"groutcode","redirect":"exam:<exam_id>","ttl_seconds":120}'
```
Redirect the browser to `data.launch_url`. Mint per click; tokens are single-use and expire ≤ 300 s. Docs: /developer/documentation/people/sso/

## create-exam
**Goal:** create and publish an exam and assign it to groups.
**Scopes:** `exams:write`, `groups:read`, `faculty:read`. **Confirmation:** no (drafts) / yes if `publish:true` emails students.
```bash
curl -s -X POST $API/exams -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' -d '{
  "faculty_id":"<faculty login_email>","title":"<title>","exam_type":"exam","max_score":100,
  "due_date":"<ISO>","time_limit_minutes":90,"proctoring":{"camera":true,"screen":true},
  "ai_policy":{"allowed":true,"model_tier":"local","max_interactions":20},
  "publish":false,"group_ids":["<group id>"]}'
```
Publish later with `POST /exams/{id}/publish`. Docs: /developer/documentation/exams/overview/

## create-coding-exam
**Goal:** create a GroutCode exam with starter files and encrypted hidden tests.
**Scopes:** `exams:write`. **Confirmation:** no.
1. `POST /exams/{id}/files` (multipart `file=`) for `starter.zip` and `hidden-tests.zip.enc` → keep each `ref`.
2. `PATCH /exams/{id}` with `code_spec` (toolchain, starter, sample_tests, hidden_tests{file, iv_b64, sha256}, commands, ai_policy).
3. `PUT /exams/{id}/code-secret {"hidden_key_b64":"<base64 32 bytes>"}`.
4. `POST /exams/{id}/publish`, `POST /exams/{id}/assignments {"group_ids":[…]}`.
Never log the key. Docs: /developer/documentation/exams/groutcode/

## grade-submission
**Goal:** record a score or request a resubmission.
**Scopes:** `submissions:write`. **Confirmation:** yes when overriding an existing grade.
```bash
curl -s -X POST $API/submissions/<id>/grade -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
  -d '{"score":87,"feedback":"<text>"}'      # score -1 = resubmission required
```
Docs: /developer/documentation/exams/submissions/

## export-results
**Goal:** get all scores for an exam as CSV.
**Scopes:** `submissions:read`. **Confirmation:** no.
```bash
curl -s $API/exams/<id>/export.csv -H "Authorization: Bearer $KEY" -o results.csv
```

## review-proctoring
**Goal:** flag attempts with suspicious activity.
**Scopes:** `submissions:read`. **Confirmation:** no.
For each submission: `GET /submissions/{id}/events` → use `counts.focus_lost`, presence of `recorder_error`, and `attempt.hidden_released_at` relative to `submitted_at`. Report, do not judge. Docs: /developer/documentation/exams/proctoring/

## credit-merits
**Goal:** fund a student's AI usage from the institution allowance.
**Scopes:** `merits:write`. **Confirmation:** **yes** (spends allowance).
```bash
curl -s $API/merits/allowance -H "Authorization: Bearer $KEY"
curl -s -X POST $API/merits/credits -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
  -d '{"email":"<login_email>","amount_cents":500,"reason":"<why>","ref":"<unique ref>"}'
```
Idempotent on `ref`. Docs: /developer/documentation/learning/merits/

## invite-guardian
**Goal:** link a parent to a student and email them an invitation.
**Scopes:** `guardians:write`. **Confirmation:** no.
```bash
curl -s -X POST $API/guardians -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
  -d '{"student_id":"<id>","guardian_email":"<email>","guardian_type":"mother","guardian_name":"<name>"}'
```
Docs: /developer/documentation/people/guardians/

## enroll-alpha
**Goal:** enroll students in an Alpha Learning curriculum.
**Scopes:** `alpha:write`. **Confirmation:** no.
```bash
curl -s $API/alpha/curricula -H "Authorization: Bearer $KEY"
curl -s -X POST $API/alpha/enrollments -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
  -d '{"curriculum_id":"<id>","student_ids":["…"],"daily_target_minutes":120}'
```
Docs: /developer/documentation/learning/alpha/

## register-webhook
**Goal:** receive events instead of polling.
**Scopes:** `webhooks:write`. **Confirmation:** no.
```bash
curl -s -X POST $API/webhooks/endpoints -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
  -d '{"url":"https://<host>/hooks/grout","description":"<job>","events":["exam.*","student.created"]}'
curl -s -X POST $API/webhooks/endpoints/<id>/test -H "Authorization: Bearer $KEY"
```
Store `data.secret` once; verify signatures per /developer/documentation/webhooks/verify/.

## replay-failed-deliveries
**Goal:** re-send dead deliveries after fixing a receiver.
**Scopes:** `webhooks:write`. **Confirmation:** yes if more than 50.
```bash
curl -s "$API/webhooks/deliveries?status=dead" -H "Authorization: Bearer $KEY"
curl -s -X POST $API/webhooks/deliveries/<id>/replay -H "Authorization: Bearer $KEY"
```
Docs: /developer/documentation/webhooks/retries/

## poll-events
**Goal:** catch up on events without webhooks.
**Scopes:** `webhooks:read`. **Confirmation:** no.
```bash
curl -s "$API/events?since=<ISO>&type=exam.*&limit=200" -H "Authorization: Bearer $KEY"
```
Persist the newest `created_at` as the next `since`.

## license-housekeeping
**Goal:** find licenses about to expire and extend the ones billing approved.
**Scopes:** `licenses:write`. **Confirmation:** **yes** per extension batch.
```bash
curl -s $API/licenses/summary -H "Authorization: Bearer $KEY"
curl -s "$API/licenses?status=active&expires_before=<ISO +30d>&limit=200" -H "Authorization: Bearer $KEY"
curl -s -X POST $API/licenses/<id>/extend -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' -d '{"days":365}'
```
Docs: /developer/documentation/people/licenses/
