Errors
Wire format and conventional status codes
The StarPlan API uses standard HTTP status codes plus a wrapped JSON body for any non-2xx response.
Envelope
{
"error": {
"code": "ERROR_CODE",
"message": "Human-readable message",
"details": {}
}
}The envelope is produced by the global HttpExceptionFilter in the backend — it is the same shape used by every endpoint on api.hfu.digital, so a single error handler in your client handles StarPlan responses uniformly with the rest of the platform.
details may be {}, an object describing the validation failure, or a small structured payload. Do not depend on its shape changing — only code and message are stable contract.
Conventional status codes
| Status | When you see it |
|---|---|
400 Bad Request | Body validation failed (e.g. webhook URL is not a URL, secret too short, invalid eventTypes). The handler also returns { data: [], error: 'Invalid since timestamp' } from GET /v1/starplan/changes when since is unparseable — this is one of the few endpoints where the error rides inside the data envelope. |
401 Unauthorized | Missing or invalid X-API-Key on an authenticated route. Messages are Missing X-API-Key header or Invalid or revoked API key. |
403 Forbidden | You authenticated successfully but the resource is not yours (e.g. fetching another developer's webhook by id). |
404 Not Found | Unknown id (program, semester, course, room, instructor, change, webhook), or no upstream URL exists for the requested semester yet. |
409 Conflict | POST /v1/starplan/developers/register with an email already in use. |
429 Too Many Requests | Rate-limit window exceeded. Standard Retry-After header. See Rate limits. |
500 Internal Server Error | Unhandled error. Logged centrally; if it persists across retries, please report it on the linked GitHub repo. |
204 No Content
The platform's TransformInterceptor converts handlers that return null or undefined into a 204 No Content response with an empty body. In the StarPlan namespace this happens for:
DELETE /v1/starplan/webhooks/:idPOST /v1/starplan/developers/deactivatePOST /v1/starplan/webhooks/:id/test
(Both delete and the deactivate route also include a confirmation message in their JSON body, so you'll typically see a 200 with that body. The 204 path is reserved for the rare case where a handler explicitly returns nothing.)
Examples
$ curl -i https://api.hfu.digital/v1/starplan/programs/not-a-real-id
HTTP/1.1 404 Not Found
content-type: application/json
{"error":{"code":"NOT_FOUND","message":"Program not-a-real-id not found"}}$ curl -i -X POST https://api.hfu.digital/v1/starplan/developers/regenerate-key
HTTP/1.1 401 Unauthorized
content-type: application/json
{"error":{"code":"UNAUTHORIZED","message":"Missing X-API-Key header"}}