> 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/hold/place-a-hold-on-a-student.md).

# Place a hold on a student

Use this endpoint to place a hold on a student, for example to stop registration until a balance is paid. `hold_code` picks one of your institution's hold types, which sets the title, description, and the activities the hold blocks. `sis_id` is your identifier for this hold, and `student_id` is the student's SIS ID or username. Add `term_code` to limit the hold to one term.

## POST /holds/student\_holds

>

```json
{"openapi":"3.0.0","info":{"title":"Stellic for Developers","version":"1.0"},"tags":[{"name":"Hold"}],"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":{"StudentHoldRequest":{"type":"object","properties":{"student_id":{"type":"string","description":"The student's SIS ID. `POST` also accepts the student's username; `PUT` accepts only the SIS ID and reports a username under `failed`."},"hold_code":{"type":"string","description":"The code of one of your institution's hold types. It sets the hold's title, description, and blocked activities."},"note":{"type":"string","description":"Text for this student, shown with the hold type's description. Up to 300 characters. On `PUT`, an empty value leaves the current note in place."},"url_text":{"type":"string","description":"Text shown for the link. Up to 200 characters. On `PUT`, an empty value leaves the current text in place."},"url_link":{"type":"string","description":"The link shown to the student. Up to 200 characters. On `PUT`, an empty value leaves the current link in place."},"term_code":{"type":"string","description":"`POST` only (`PUT` ignores it). The SIS term code of the one term the hold applies to. When it's omitted or doesn't match a term, the hold applies to every term, with no error."}}},"StudentHoldResponse":{"type":"object","description":"A serialized hold record, returned under the `hold` key.","properties":{"activities":{"type":"array","description":"Activity names this hold blocks, as plain strings (not objects). Empty when the hold type is inactive or has no enforced activity. Possible values are `disallow_register_activity`, `disallow_drop_activity`, and `hide_grade_activity`.","items":{"type":"string"}},"campuses":{"type":"array","description":"Campuses the hold is limited to. Empty means every campus. Holds placed through the API never set this; holds loaded by your data sync can.","items":{"type":"object","properties":{"name":{"type":"string"},"screen_name":{"type":"string"}}}},"code":{"type":"string","description":"The hold type's code (the `hold_code` you sent)."},"description":{"type":"string","description":"The hold type's description. Displayed along with the title."},"enrollment_levels":{"type":"array","description":"Enrollment levels the hold is limited to. Empty means every level. Holds placed through the API never set this; holds loaded by your data sync can.","items":{"type":"object","properties":{"display_name":{"type":"string"},"name":{"type":"string"}}}},"note":{"type":"string","description":"The per-student note you sent, or an empty string."},"semester":{"type":"string","nullable":true,"description":"Term name when the hold is term-scoped (a matching `term_code` was sent), otherwise `null`."},"sis_id":{"type":"string","description":"The hold's `sis_id`."},"title":{"type":"string","description":"The hold type's title. Displayed to the student."},"url_link":{"type":"string","description":"The link you sent, or an empty string."},"url_text":{"type":"string","description":"The link placeholder text you sent, or an empty string."},"year":{"type":"integer","nullable":true,"description":"Term year when the hold is term-scoped, otherwise `null`."}}}}},"paths":{"/holds/student_holds":{"post":{"tags":["Hold"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"type":"object","properties":{"sis_id":{"type":"string","description":"Your identifier for this hold, up to 128 characters. It can't match an active hold; the `sis_id` of a removed hold can be reused."}}},{"$ref":"#/components/schemas/StudentHoldRequest"}],"required":["sis_id","student_id","hold_code"]}}}},"responses":{"200":{"description":"Returned for both success and for validation or domain errors. Branch\non the `success` field in the body, not the HTTP status.\n\nOn success, `success` is `true` and the created hold is under `hold`.\n\nOn error, `success` is `false` and the body is\n`{\"success\": false, \"message\": {\"code\": ..., \"text\": ...}}`.\nThe HTTP status is still `200`. `message.code` and `message.text` are:\n\n- `404` `The provided hold type is invalid.` for an unknown `hold_code`\n- `404` `Invalid student.` for an unknown `student_id`\n- `404` ``Cannot create a Student Hold that links to a student who is no longer active: `{student_id}` `` for an archived student\n- `404` `The provided student hold is invalid.` when an active hold already uses `sis_id`\n- `400` `We are unable to process your request at the moment. Please try again later.` for a missing `sis_id`, `student_id`, or `hold_code` (the field isn't named), a value longer than its limit, or any other failure","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"hold":{"$ref":"#/components/schemas/StudentHoldResponse"}}}}}},"401":{"description":"Missing or invalid Bearer token. This is the only genuine error HTTP\nstatus; auth is checked before the view runs."},"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"}}}}}}}}}}}
```

#### What happens after the call

* The hold applies right away. Stellic checks holds each time the student tries a blocked activity, and advisors can filter on it in search immediately.
* `disallow_register_activity` blocks registration, adds, and swaps through Stellic. Drops are blocked by `disallow_drop_activity` at institutions that use a separate drop hold, and by `disallow_register_activity` everywhere else. `hide_grade_activity` hides grades from the student, for holds without a term.

#### Notes

{% hint style="warning" %}
**Errors come back with HTTP 200.** Check `success` in the body. On failure, the body is `{"success": false, "message": {"code": ..., "text": ...}}`, and `message.code` holds the 400 or 404.
{% endhint %}

* **Check `semester` and `year` in the response when you send `term_code`.** A `term_code` that doesn't match a term is ignored without an error, and the hold applies to every term.
* **A hold blocks nothing if its type is inactive or has no enforced activity.** The hold is still created; `activities` in the response is empty.
* **Missing fields return a generic message.** A body without `sis_id`, `student_id`, or `hold_code` returns `message.code` 400 with "We are unable to process your request at the moment," which doesn't name the field.
* **Your data sync can remove holds placed here.** If your institution's student data sync runs, it replaces each synced student's holds with the ones it sends. Include API-placed holds in the sync, or expect them to be removed on the next run.
* **You can reuse the `sis_id` of a removed hold.** The new hold is a separate record.

#### Related APIs

* [`GET /holds/student_holds/{sis_id}`](/stellic-partner-api/hold/read-a-student-hold.md) to read the hold.
* [`PUT /holds/student_holds/{sis_id}`](/stellic-partner-api/hold/update-a-student-hold.md) to change it.
* [`DELETE /holds/student_holds/{sis_id}`](/stellic-partner-api/hold/remove-a-student-hold.md) to remove it.
