> 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/student/partial-update-of-an-existing-student-profile.md).

# Partial update of an existing student profile

Use this endpoint to change some fields on a student who already exists. There are no required fields, but the body can't be empty, and a student who doesn't exist, is archived, or is a test account returns `No student exists with username: {username}`. To create students, use [`PUT /student/{student_username}/profile/`](/stellic-partner-api/student/upsert-a-student-profile.md).

## PATCH /student/{student\_username}/profile/

>

```json
{"openapi":"3.0.0","info":{"title":"Stellic for Developers","version":"1.0"},"tags":[{"name":"Student"}],"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":{"StudentProfilePatchRequestBody":{"type":"object","description":"At least one field must be sent. An empty body returns `400 request body with student information is required.` Omitted name, email, and pronoun fields are unchanged. The mobile number is unchanged when both `mobile_number` and `text_consent` are omitted. Values are matched the same way as on `PUT`.","properties":{"first_name":{"type":"string","nullable":true,"description":"First name of the student. Omitting it leaves it unchanged; sending `null` or an empty string saves it as empty unless a non-empty `preferred_first_name` is also sent."},"preferred_first_name":{"type":"string","description":"A non-empty value is saved as the student's first name in place of `first_name`. An empty value sent without `first_name` leaves the stored first name unchanged. To clear the first name, send `first_name` as `null` or an empty string without a non-empty `preferred_first_name`."},"last_name":{"type":"string","nullable":true,"description":"Last name of the student. Omitting it leaves it unchanged; sending `null` saves it as empty."},"email":{"type":"string","nullable":true,"description":"Email address. Lowercased before it's saved. Omitting it leaves it unchanged; sending `null` saves it as empty."},"legal_name":{"type":"string","nullable":true,"description":"Legal name of the student. Saved on PATCH. Sending `null` saves it as empty; omitting it leaves it unchanged."},"primary_advisor_username":{"type":"string","description":"Username of the primary advisor. Lowercased unless your institution signs users in with an identifier other than the username, in which case the case you send must match the advisor's record. An unknown username creates an advisor account after validation and any entry-year change succeed. You can't clear the advisor through this endpoint. Sending the student's own username leaves the advisor unchanged and reports `{username} is an invalid primary_advisor_username value: the student themselves` in `errors` on a successful response."},"primary_campus_id":{"type":"string","nullable":true,"description":"The primary campus's name, lowercased before it's matched. A value that doesn't match a campus is reported in `errors` and leaves the stored campus unchanged. Sending `null` or an empty string clears the primary campus."},"pronouns":{"type":"string","nullable":true,"description":"Omitting it leaves it unchanged; sending `null` saves it as empty."},"current_campus_id":{"type":"string","description":"The campus name for the student's current term, lowercased before it's matched. Saved only when it differs from both the `primary_campus_id` in the same body and the student's primary campus after applying any primary-campus change in that request; it then replaces the campus recorded for the student's current term (one campus per term). Sending only `current_campus_id` is fine."},"expected_graduation":{"type":"string","nullable":true,"description":"The SIS term code of the student's expected graduation term (for example `spring-2028`). A code that doesn't match a term is reported in `errors` and leaves the stored term unchanged. Sending `null` or an empty string clears it."},"class_level_id":{"type":"string","description":"The class level's short name. A matching value updates the student's class level. Sending `null` or an empty string returns a 400 with `class_level is a required field.` in `errors`."},"pre_req_waivers":{"type":"array","items":{"type":"string"},"description":"Course codes of the courses whose prerequisites are waived for the student. Replaces the full set; an entry that doesn't match is reported in `errors` and left out. Must be a list; other types, including `null`, return a 400."},"school_ids":{"type":"array","items":{"type":"string"},"description":"School short names. Replaces the full set; an entry that doesn't match is reported in `errors` and that link is removed. Must be a list; other types, including `null`, return a 400."},"department_ids":{"type":"array","items":{"type":"string"},"description":"Department short names. Replaces the full set; an entry that doesn't match is reported in `errors` and that link is removed. Must be a list; other types, including `null`, return a 400."},"term_joined":{"type":"string","description":"The SIS term code of the student's entry term. A code that doesn't match a term returns a 400. Sending `null` or an empty string returns a 400 with `term_joined is a required field.` in `errors`. When the derived entry year changes, plan items' relative study-year positions are recalculated during the call rather than shifted to different calendar terms. See the page's Notes for the planned items removed by this change."},"catalog_term":{"type":"string","nullable":true,"description":"The SIS term code of the student's catalog term. A code that doesn't match a term is reported in `errors` and leaves the stored catalog term unchanged. Sending `null` or an empty string clears it."},"ge_catalog_term":{"type":"string","description":"Alias for `catalog_term`. When both are sent, `ge_catalog_term` is used."},"year_begin":{"type":"integer","description":"The student's entry year. Ignored when `term_joined` is sent, since the entry year then comes from that term. Sending a different year without `term_joined` returns a 500 with `Stellic couldn't process your request` in `errors` without saving profile changes. Send `term_joined` instead."},"student_id":{"type":"string","nullable":true,"description":"The student ID shown in Stellic. Saved on PATCH. Sending `null` clears it; omitting it leaves it unchanged. A non-empty value also updates the identifier used by your learning management system integration at institutions configured to match that integration on the student ID."},"type":{"type":"string","nullable":true,"description":"The student type's name. A matching value updates the student's type. Sending `null` or an empty string clears it."},"uni_id":{"type":"string","description":"The student ID shown in Stellic (the value `student_id` sets on PUT)."},"sis_id":{"type":"string","nullable":true,"description":"The identifier your learning management system integration matches the student on. Sending `null` saves it as an empty string; omitting it leaves it unchanged. If `student_id` is also sent, `sis_id` wins. A non-string value, or one longer than 30 characters, returns a 400."},"permission_section_ids":{"type":"array","items":{"type":"string"},"description":"Catalog IDs of the sections the student can register for past section restrictions. Replaces the full set; an entry that doesn't match exactly one section is reported in `errors` and that link is removed. Send `[]` to clear it. Must be a list; other types, including `null`, return a 400."},"mobile_number":{"type":"string","nullable":true,"description":"Saved only when `text_consent` is `true` in the same body. If either `mobile_number` or `text_consent` is sent without true consent, the stored number is cleared. Omitting both leaves it unchanged. Sending `null` for `mobile_number` clears it."},"text_consent":{"nullable":true,"oneOf":[{"type":"boolean"},{"type":"string"}],"description":"Consent to text messages, which `mobile_number` requires. Send `true` (a boolean, or the string `\"true\"` in any case). Other accepted values clear the mobile number. A non-null value that isn't a boolean or a string returns a 400 with `text_consent must be a boolean or a string` in `errors`."},"primary_enrollment_level_id":{"type":"string","nullable":true,"description":"The enrollment level's name. A matching value sets the student's primary enrollment level, including when no primary level exists. Sending `null` or an empty string leaves the stored primary enrollment level unchanged."}}},"StudentProfilePatchSuccess":{"type":"object","description":"Profile snapshot returned by PATCH. Same shape regardless of which fields were sent.","properties":{"status":{"type":"string"},"state":{"type":"object","properties":{"username":{"type":"string"},"first_name":{"type":"string"},"last_name":{"type":"string"},"email":{"type":"string"},"primary_advisor":{"type":"string","nullable":true,"description":"Username of the primary advisor, or `null` if unset."},"pronouns":{"type":"string","nullable":true},"departments":{"type":"array","items":{"type":"string"},"description":"Short names of the departments currently linked to the student."},"schools":{"type":"array","items":{"type":"string"},"description":"Short names of the schools currently linked to the student."},"year_begin":{"type":"integer","nullable":true,"description":"The student's entry year."},"campus":{"type":"string","nullable":true,"description":"The primary campus's display name, or `null` if unset."},"expected_graduation":{"type":"string","nullable":true,"description":"SIS term code of the expected graduation term (for example `spring-2028`), or `null` if unset."},"term_joined":{"type":"string","nullable":true,"description":"SIS term code of the entry term, or `null` if unset."},"catalog_term":{"type":"string","nullable":true,"description":"SIS term code of the catalog term, or `null` if unset."},"student_id":{"type":"string","nullable":true,"description":"The student ID shown in Stellic (set through `student_id` or `uni_id` on PATCH)."},"sis_id":{"type":"string","description":"The identifier your learning management system integration matches the student on."},"mobile_number":{"type":"string","nullable":true},"permission_section_ids":{"type":"array","items":{"type":"string"},"description":"Catalog IDs of the sections the student can register for past section restrictions."}}},"errors":{"type":"array","items":{"type":"string"},"description":"On successful responses, one plain-string message per non-blocking lookup failure (for example `invalid class_level_id value` or `{value} is an invalid school_ids value`). Can be non-empty when `status` is \"success\"."}}},"StudentLinkResponseFailure":{"allOf":[{"$ref":"#/components/schemas/GenericResponseFailure"},{"type":"object","properties":{"identifiers":{"type":"object","properties":{"student_username":{"type":"string"}}}}}]},"GenericResponseFailure":{"type":"object","properties":{"status":{"type":"string"},"message":{"type":"string"}}}}},"paths":{"/student/{student_username}/profile/":{"patch":{"tags":["Student"],"parameters":[{"name":"student_username","in":"path","required":true,"description":"Student's platform username. Lowercased before lookup unless your institution signs students in with an identifier other than the username, in which case the case you send must match the record. Archived students and test accounts return the same `No student exists` 400 as a missing user.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StudentProfilePatchRequestBody"}}}},"responses":{"200":{"description":"Student profile updated. Response body is the student's profile snapshot: `username`, `first_name`, `last_name`, `email`, `primary_advisor`, `pronouns`, `departments`, `schools`, `year_begin`, `campus`, `expected_graduation`, `term_joined`, `catalog_term`, `student_id`, `sis_id`, `mobile_number`, `permission_section_ids`, plus a top-level `status` and an `errors` array. Read `errors` on every successful PATCH response: non-blocking lookup failures land here as plain strings while `status` remains \"success\". An unmatched `term_joined` instead rejects the request with a 400.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StudentProfilePatchSuccess"}}}},"400":{"description":"Student not found or input validation failure. The response body is not uniform: input validation failures land as `{\"status\": \"failure\", \"identifiers\": {...}, \"errors\": [{\"message\": \"...\"}]}`, while missing-body and student-not-found failures come back as `{\"status\": \"failure\", \"identifiers\": {...}, \"message\": \"...\"}` with a single string and no `errors` array. Input validation failures reject the request before profile changes are saved.\n\nCommon causes:\n- Missing request body or empty body `{}` - returns `request body with student information is required.`\n- Student not found, archived, or flagged as a test account - returns `No student exists with username: {username}` (all three causes share this message)\n- `term_joined` that doesn't match a term - returns `{\"message\": \"invalid term id value for term_joined\"}` and `{\"message\": \"term_joined is a required field.\"}` in `errors`, plus a message for each other value in the body that didn't match.\n- `term_joined` or `class_level_id` sent as `null` or an empty string - returns `term_joined is a required field.` or `class_level is a required field.` in `errors`.\n- `department_ids`, `school_ids`, `pre_req_waivers`, or `permission_section_ids` sent as anything other than a list, including `null` - returns `{field} must be a list` in `errors`.\n- `sis_id` sent as anything other than a string or `null` - returns `sis_id must be a string` in `errors`; a `sis_id` longer than 30 characters returns `sis_id must be at most 30 characters`.\n- Non-null `text_consent` sent as anything other than a boolean or a string - returns `text_consent must be a boolean or a string` in `errors`.\n\nOther values that don't match don't return `400`; they land in `errors` on a `200`. Most are left out of the update: an unmatched `primary_campus_id`, `catalog_term`, or `expected_graduation` keeps the stored value. An unmatched list entry removes that link.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/StudentLinkResponseFailure"},{"type":"object","properties":{"errors":{"type":"array","description":"Present instead of `message` for input validation failures.","items":{"type":"object","properties":{"message":{"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"}}}}}},"500":{"description":"An entry-year change that cannot be completed returns `{\"status\": \"failure\", \"identifiers\": {...}, \"errors\": [{\"message\": \"Stellic couldn't process your request\"}]}` without saving profile changes. Sending a different `year_begin` without `term_joined` takes this path; send `term_joined` instead.\n\nOther unhandled server errors during the update have no dedicated error body, so do not parse their body. Nothing from the request is saved. Otherwise, log the request and reach out if you need to diagnose."}}}}}}
```

#### What happens after the call

* The fields you send are saved right away, all together: if the update fails partway, nothing from the request is saved. Values are matched the same way as on [`PUT`](/stellic-partner-api/student/upsert-a-student-profile.md#how-values-are-matched). On successful requests, values that don't match are reported in `errors` with a 200; an unmatched `term_joined` rejects the request with a 400 instead.
* After a successful profile update, access is recalculated in a background task, which queues an update to the student's search information.
* Changes to an existing student's `permission_section_ids` are recorded in their change history right away, the same way your data sync records them.
* When `term_joined`, `catalog_term`, `primary_campus_id`, `school_ids`, `department_ids`, `type`, or `primary_enrollment_level_id` change, general requirements are recalculated in the background. General requirement matching is restricted to primary plans, regardless of the student's default-plan selection. On a prospective instance, the initial selection uses empty plans, which do not qualify for this matching. If no plan is selected for the updated student, the recalculation can also change general requirements on other students' primary plans. Contact your Stellic Representative before sending these changes if you are unsure whether the student has a qualifying plan. Non-archived audits with a matching or already attached general requirement can be marked for refresh, even if the requirement itself does not change. If you use the pathway experience, automatically applied pathways are recalculated in the background after the requirements recalculation succeeds. For pathway reconciliation, the updated student's plan must be non-archived, marked as default, and of the plan type selected for your setup. A default plan of another type is not reconciled. Ask your Stellic Representative which plan type your setup selects.
* A disabled student is re-enabled. An inactive account is activated only if it meets your activation criteria. An already-active account is not deactivated by this update if it no longer meets those criteria. Sign-in also depends on authentication.

#### Notes

* **Leave unchanged fields out.** You don't need to resend the student's name, email, pronouns, or phone with unrelated changes.
* **A sent `sis_id` wins over `student_id`.** Omitting `sis_id` preserves it unless your institution's learning management system matching configuration derives it from a non-empty `student_id` you send. When you send both, `sis_id` is saved as sent, whatever their order in the request. Sending `sis_id: null` clears it.
* **List fields replace the whole set.** `school_ids`, `department_ids`, `pre_req_waivers`, and `permission_section_ids` become exactly what you send, and an entry that doesn't match removes that link. Send a list, even for one value, and send `[]` to clear `permission_section_ids`.
* **Don't use `current_campus_id` to reset a campus override.** Sending `null`, an empty string, or a value equal to the primary campus is a no-op for the current-term campus override. An existing override remains unchanged; it does not reset to the primary campus. This endpoint provides no explicit operation to clear an existing current-term campus override.
* **Changing `term_joined` can remove planned items.** When the entry year changes, unregistered courses in past calendar terms and past-term placeholders are removed during the call. Retained courses and placeholders keep their calendar terms; their relative study-year positions are recalculated against the new entry year. Check the student's plan before changing their entry term. To change the entry year, send `term_joined` rather than `year_begin` alone.

#### Related APIs

* [`PUT /student/{student_username}/profile/`](/stellic-partner-api/student/upsert-a-student-profile.md) to create a student or send their full profile.
* [`PUT /student/{student_username}/state/`](/stellic-partner-api/student/archive-or-restore-a-student.md) to restore an archived student before updating them.
* [`POST /student/{student_username}/school/{school_id}`](/stellic-partner-api/student/link-a-student-to-a-school.md) and [`POST /student/{student_username}/department/{dept_id}`](/stellic-partner-api/student/link-a-student-to-a-department.md) to add one school or department.
