Course settings JSON API
Hanami exposes a versioned, teacher-only JSON interface for configuration automation. Version 1 has one resource:
/api/v1/courses/{courseId}/settingsAuthenticate with an A+ API token belonging to a teacher of the configured course. Keep the token outside scripts and shell history:
export HANAMI_URL=https://hanami.example.orgexport APLUS_TOKEN='...'
curl --fail-with-body \ -H "Authorization: Token $APLUS_TOKEN" \ "$HANAMI_URL/api/v1/courses/123/settings"GET returns the writable settings together with revision, courseId, and
the server-owned tags and ltiLoginUrl fields. Its ETag header contains the
same revision.
Replace settings
Section titled “Replace settings”PUT replaces every writable field. Start from a GET response, remove
revision, courseId, tags, and ltiLoginUrl, edit the remaining JSON, and
send the revision as a strong If-Match header:
curl --fail-with-body \ -X PUT \ -H "Authorization: Token $APLUS_TOKEN" \ -H 'Content-Type: application/json' \ -H 'If-Match: "4"' \ --data-binary @course-settings.json \ "$HANAMI_URL/api/v1/courses/123/settings"The request object has these fields:
| Field | Type | Notes |
|---|---|---|
schemaVersion |
integer | Currently 1. |
slug |
string | Current lowercase URL slug. It must be sent unchanged. |
name |
string | Non-empty course name. |
languages |
string array | At least one unique content language. |
highlightLanguages |
string array | Unique supported syntax highlighters. |
courseConfigUrl |
string or null |
HTTPS or HTTP course configuration URL. |
templates |
object | automaticExtensions, extensions, and excludedPathSegments. |
listed |
boolean | Whether students can discover the course. |
visibleTagSlugs |
string array | Every slug must exist in the read-only tags list. |
graders |
object array | Each entry has userId, languages, feedbackTags, and gradingTags. |
manualGradingExercises |
integer array | Unique positive A+ exercise ids. |
Unknown fields are rejected. Hanami also rejects grader language or tag references that are not configured, and verifies that every grader is current A+ course staff. Refresh A+ tags and LTI metadata in the Hanami settings UI before using newly created tags through this API.
The request body limit is 2 MB. A successful PUT returns the updated resource
and its new ETag. If another writer changed the course, the request returns
412; fetch the current document, reconcile the changes, and retry. Omitting
If-Match returns 428.
Errors
Section titled “Errors”Version 1 errors always use one envelope. Validation errors include stable JSON paths and machine-readable issue codes:
{ "error": { "code": "validation_failed", "message": "Request validation failed", "requestId": "9cb32531-bab4-4e8e-8887-30dd0d170945", "issues": [ { "path": "$.visibleTagSlugs", "code": "invalid_value", "message": "visible tag is unknown" } ] }}Clients should branch on error.code and issues[].code, not the human-readable
messages. Relevant status codes are 400 for malformed or invalid input, 401
for an invalid token, 403 when the caller does not teach the requested course,
404 when a taught course has no Hanami configuration, 412 for a stale
revision, 413 for an oversized body, 415 for a non-JSON body, and 428 when
If-Match is missing.