> For the complete documentation index, see [llms.txt](https://docs.stellic.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.stellic.com/stellic-partner-api/plan-reviews/list-students-with-reviewed-plans.md).

# List students with reviewed plans

Use this endpoint to see where each student's primary plan stands in advisor review: whether it's marked reviewed now, who reviewed it and when, and when the student or a staff member last changed it. You get one row per active student, 10 per page by default (up to 100 with `page_size`), with `count` as the total. Despite the name, everyone is listed unless you pass `only_reviewed=true`.

## GET /planner/student\_plan\_reviews

>

```json
{"openapi":"3.0.0","info":{"title":"Stellic for Developers","version":"1.0"},"tags":[{"name":"Plan Reviews","description":"Which students' primary plans have been marked reviewed by an advisor, who reviewed them, and when they last changed."}],"servers":[{"url":"https://{institution}.stellic.com/partnerapi/v1","variables":{"institution":{"default":"{institution}","description":"Your institution's Stellic subdomain: the part before `.stellic.com` in the URL you use to sign in. It becomes the host in every request, as in `https://{institution}.stellic.com/partnerapi/v1/...`. Replace the `{institution}` placeholder with your subdomain before sending."}}}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Personal Access Token issued by your Stellic integration partner. Send it as `Authorization: Bearer <token>` on every request. One token per institution; do not share across environments."}},"schemas":{"PlanReviewListResponse":{"type":"object","properties":{"success":{"type":"boolean"},"count":{"type":"integer","description":"Total number of matching plans across all pages (one per student), not the number of rows on this page."},"students":{"type":"array","description":"Rows for this page, ordered by the plan's internal ID (roughly plan creation order), stable across pages.","items":{"$ref":"#/components/schemas/PlanReviewEntry"}}}},"PlanReviewEntry":{"type":"object","description":"One student's primary plan. Every field is a string; a value that is not set comes back as an empty string, never `null`. Timestamps use the format `YYYY-MM-DD HH:MM:SS UTC`.","properties":{"student_username":{"type":"string"},"plan_status":{"type":"string","description":"`verified` means the plan is currently marked reviewed. Anything else (in practice `draft`) means it is not."},"plan_name":{"type":"string"},"reviewer_username":{"type":"string","description":"Username of the staff member who most recently marked the plan reviewed. Stays populated after the plan drops back to `draft`. Empty string if the plan has never been reviewed."},"reviewed_at":{"type":"string","description":"When the plan was most recently marked reviewed. Stays populated after the plan drops back to `draft`; check `plan_status` for the current state. Empty string if never reviewed."},"created_at":{"type":"string","description":"When the plan was created."},"last_modified_by_student_at":{"type":"string","description":"Time of the most recent change the student made to the plan, taken from the plan's activity history. Changes that arrive through the data feed do not move it. Empty string if none."},"last_modified_by_admin_at":{"type":"string","description":"Time of the most recent change a staff member made to the plan, taken from the plan's activity history. Changes that arrive through the data feed do not move it. Empty string if none."}}}}},"paths":{"/planner/student_plan_reviews":{"get":{"tags":["Plan Reviews"],"parameters":[{"name":"only_reviewed","in":"query","required":false,"description":"Pass the literal `true` to list only plans currently marked reviewed (`plan_status` is `verified`). Defaults to `false`, which lists every active student's primary plan whether or not it has ever been reviewed. Any value other than `true` (in any letter case) counts as `false`; `1` and `yes` do not work.","schema":{"type":"string"}},{"name":"only_primary_plan","in":"query","required":false,"description":"Accepted for backward compatibility. The list already contains only each student's primary plan, so `true` changes nothing on a standard institution. On an institution configured for prospective students, where the default plan is a different plan type, `true` returns an empty list.","schema":{"type":"string"}},{"name":"student_username","in":"query","required":false,"description":"Restrict the list to one student. Matched exactly as stored; not lowercased. A username that does not exist, or that belongs to an archived student or a test account, returns `200` with `count: 0`, not an error.","schema":{"type":"string"}},{"name":"start_date","in":"query","required":false,"description":"Keep plans whose most recent review (`reviewed_at`) is at or after this moment. Send `YYYY-MM-DD` or an ISO 8601 date-time, such as `YYYY-MM-DD HH:MM:SS`. A date-time without a time zone is interpreted as UTC; one with a UTC offset is converted to UTC. A date without a time means midnight at the start of that day in UTC. A valid, non-empty value excludes plans that have never been reviewed, even with `only_reviewed=false`. Omitted or empty values apply no start boundary. A non-empty value the platform cannot parse returns `400` with an error naming `start_date`.","schema":{"type":"string"}},{"name":"end_date","in":"query","required":false,"description":"Keep plans whose most recent review (`reviewed_at`) is at or before this moment. Accepts the same formats as `start_date`. A date-time without a time zone is interpreted as UTC; one with a UTC offset is converted to UTC. A date without a time means the last microsecond of that day in UTC, so `2026-01-31` includes reviews throughout January 31. A valid, non-empty value excludes plans that have never been reviewed, even with `only_reviewed=false`. Omitted or empty values apply no end boundary. A non-empty value the platform cannot parse returns `400` with an error naming `end_date`.","schema":{"type":"string"}},{"name":"page","in":"query","required":false,"description":"Page number, 1-indexed. Defaults to 1. `last` jumps to the final page. Zero, a negative number, a non-numeric value, or a number past the last page returns `404`.","schema":{"type":"integer"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Defaults to 10; values above 100 are capped at 100. Zero, negative, or non-numeric values fall back to 10.","schema":{"type":"integer"}}],"responses":{"200":{"description":"Success. `count` is the total number of matching plans across all pages (one per student), not the size of this page. There are no next or previous links; step `page` until `page * page_size` reaches `count`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanReviewListResponse"}}}},"400":{"description":"A non-empty `start_date` or `end_date` cannot be parsed. The error names the invalid parameter. If both are invalid, `start_date` is reported first.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"error":{"type":"string"}}}}}},"401":{"description":"Missing or invalid Bearer token."},"403":{"description":"Your IP address isn't on the allowlist for this endpoint. Contact Stellic to add it; the response includes the `client_ip` Stellic saw.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"},"client_ip":{"type":"string"}}}}}},"404":{"description":"Invalid `page` (zero, negative, non-numeric, or past the last page).\n\nBody: `{\"detail\": \"Invalid page.\"}`","content":{"application/json":{"schema":{"type":"object","properties":{"detail":{"type":"string"}}}}}}}}}}}
```

#### Notes

* **`plan_status` is the current state.** `verified` means reviewed. `draft` means the plan has never been reviewed or was un-reviewed later, and un-reviewing doesn't clear `reviewed_at` or `reviewer_username`, so treat those as the most recent review.
* **Compare `last_modified_by_student_at` with `reviewed_at`** to find plans a student changed after review. Both modified times record changes made in Stellic, not ones from your data sync, so a plan nobody has touched shows `""` for both.
* **Valid, non-empty date filters drop plans that were never reviewed.** `start_date` and `end_date` compare against `reviewed_at`, even with `only_reviewed=false`. For a review window covering whole days, send the first and last day's dates rather than advancing `end_date` to the next day.
* **Flags are on only for the word `true`,** in any case. `1` and `yes` count as `false`.
* **Every value is a string, and an unset one is `""`, never `null`.** Timestamps look like `2026-01-31 14:05:00 UTC`.
* **An unknown, archived, or test-account username returns `count: 0`,** not an error. Only active students' primary plans are listed.
* **Page through by `page` until `page * page_size` reaches `count`.** Rows are in a stable order. A page past the end returns a 404, `{"detail": "Invalid page."}`.

#### Related APIs

* [`GET /student/{student_username}/planner/primary/{course_filter}/{property}`](/stellic-partner-api/student/read-courses-from-a-student-s-primary-planner.md) to read the courses on a plan.
* [`GET /student/{student_username}/progress/summary`](/stellic-partner-api/student/read-a-student-s-progress-summary.md) for the student's progress alongside the review state.
