Authentication
When the StarPlan API needs an API key — and when it doesn't
The StarPlan API uses a single authentication scheme: an X-API-Key HTTP header carrying a developer-scoped key.
Anonymous vs authenticated routes
| Route group | Authentication |
|---|---|
GET /v1/starplan/programs and all other catalog reads | None — anonymous |
GET /v1/starplan/semesters / /courses / /rooms / /instructors / /search / /statistics / /sync/status | None — anonymous |
GET /v1/starplan/week/* | None — anonymous |
GET /v1/starplan/ical/:semesterId and /url | None — anonymous |
GET /v1/starplan/changes and /changes/:id | None — anonymous |
POST /v1/starplan/developers/register | None — anonymous |
GET /v1/starplan/developers/me | X-API-Key required |
POST /v1/starplan/developers/regenerate-key | X-API-Key required |
POST /v1/starplan/developers/deactivate | X-API-Key required |
GET /v1/starplan/developers/usage | X-API-Key required |
* /v1/starplan/webhooks* (all methods) | X-API-Key required |
Key format
API keys have the prefix spk_ followed by 24 random base64url-encoded bytes (32 characters), giving a 36-character total length. Example shape: spk_AbCdEfGh….
The server stores only:
- A bcrypt hash (10 rounds) of the full key.
- A 4-character hint equal to the last 4 characters of the raw key, for fast bucket lookup at validation time.
There is no way to recover a lost key — only rotate it via POST /v1/starplan/developers/regenerate-key (which itself requires the current key).
Sending the header
curl https://api.hfu.digital/v1/starplan/developers/me \
-H 'X-API-Key: spk_AbCd1234…'await fetch('https://api.hfu.digital/v1/starplan/developers/me', {
headers: { 'X-API-Key': process.env.STARPLAN_API_KEY },
});The header name is case-insensitive (x-api-key works too).
Failure modes
| Condition | Response |
|---|---|
| Header missing on an authenticated route | 401 Unauthorized — Missing X-API-Key header |
| Header present but key is unknown / revoked / inactive | 401 Unauthorized — Invalid or revoked API key |
Key prefix is not spk_ | 401 Unauthorized (rejected before the bcrypt compare) |
Anonymous developer endpoints
Two endpoints in the /developers group accept anonymous traffic:
POST /v1/starplan/developers/register— issues a new key. Anyone can call it; the response contains the only copy of the raw key you'll ever see.- The
registerendpoint enforces a uniqueemail(case-sensitive). A second registration with the same email returns409 Conflict — Email already registered.
All other /developers/* routes require the requester's own key.
Webhook signatures vs API keys
Webhook deliveries from StarPlan to your server use a separate, per-webhook secret that you set when creating the webhook. That secret signs each delivery with HMAC-SHA256 (X-Webhook-Signature header) — it is unrelated to your developer API key. See Webhooks.
Best practices
- Treat
spk_…like a password: never commit it, never put it in client-side code, never log it. Only the last four characters are safe to surface (the API itself does this viaapiKeyHint). - Rotate on suspicion of leakage. The new key invalidates the old one immediately.
- Deactivate keys you no longer need (
POST /v1/starplan/developers/deactivate) — there is no per-key TTL, so unused keys live forever until rotated or deactivated.