{"openapi":"3.1.0","info":{"title":"CodeTrain API","description":"The public CodeTrain API: the course catalog, the changelog and the roadmap, read-only and with\nno authentication. Everything here is also published as a page on https://codetrain.ai, so this\nexists to save you scraping HTML.\n\n**Rate limits.** Every response carries `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset`\nand `RateLimit-Policy`. A `429` also carries `Retry-After`. Throttle from the headers rather than\nfrom the refusal.\n\n**Versioning.** The major version is in the path. New endpoints and new fields on an existing\nresponse can appear inside `/v1` at any time, so ignore what you do not recognise. Nothing is\nremoved or renamed inside a version: a breaking change means `/v2` and `/v1` keeps working. An\nendpoint on its way out answers with `Deprecation` and `Sunset` headers at least 90 days ahead,\nand the same notice goes in the changelog.\n\n**What is not here.** CodeTrain has an authenticated API behind the dashboard and the CLI. It is\nnot part of this contract, is versioned separately, and is deliberately not published: it is an\nimplementation detail of our own clients. If you need something from it, write to\nsupport@codetrain.ai and say what you are trying to do.\n\nHuman reference: https://codetrain.ai/developers/\n","contact":{"name":"CodeTrain support","url":"https://codetrain.ai/contact/","email":"support@codetrain.ai"},"license":{"name":"Terms of service","url":"https://codetrain.ai/terms"},"version":"1.0.0"},"paths":{"/health":{"get":{"tags":["health"],"summary":"Service health","description":"Whether the service is answering. Touches no database and is cheap to poll.","operationId":"health","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":{"type":"string"},"type":"object","title":"Response Health"}}}}}},"head":{"tags":["health"],"summary":"Service health (HEAD)","description":"The same check as GET /health, for uptime monitors that probe with HEAD.","operationId":"healthHead","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":{"type":"string"},"type":"object","title":"Response Healthhead"}}}}}}},"/v1/health":{"get":{"tags":["public-api"],"summary":"Service health","description":"Whether the API is answering, and which version is serving. Cheap enough to poll, and it touches no database.","operationId":"getHealth","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}},"429":{"description":"Rate limit exceeded. Wait the number of seconds in `Retry-After`. Every response, including this one, carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`."}}}},"/v1/catalog":{"get":{"tags":["public-api"],"summary":"List every course","description":"The full CodeTrain catalog: courses, the classes inside them, and the lessons inside those, with counts. The same data that renders at https://codetrain.ai/learn/. Curated courses are a starting point rather than the product, so treat this as a sample of what CodeTrain teaches, not a limit: the tutor writes a lesson for any topic or any public repository on request.","operationId":"listCatalog","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Catalog"}}}},"429":{"description":"Rate limit exceeded. Wait the number of seconds in `Retry-After`. Every response, including this one, carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`."}}}},"/v1/courses/{course_id}":{"get":{"tags":["public-api"],"summary":"Get one course","description":"One course with its classes and their lessons. 404 when no course has that id.","operationId":"getCourse","parameters":[{"name":"course_id","in":"path","required":true,"schema":{"type":"string","description":"Course id, as returned by /v1/catalog.","title":"Course Id"},"description":"Course id, as returned by /v1/catalog."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Course"}}}},"429":{"description":"Rate limit exceeded. Wait the number of seconds in `Retry-After`. Every response, including this one, carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`."},"404":{"description":"No such record. A miss is a 404, never a 200 with an empty body."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/lessons/{lesson_id}":{"get":{"tags":["public-api"],"summary":"Get one lesson's metadata","description":"Title, blurb, runtime and step count for a single lesson. It does NOT return the steps. Writing them is what the learner is here to do, and handing the curriculum to an unauthenticated caller would give away the product and the answer in the same response. 404 when no lesson has that id.","operationId":"getLesson","parameters":[{"name":"lesson_id","in":"path","required":true,"schema":{"type":"string","description":"Lesson id, as returned by /v1/catalog.","title":"Lesson Id"},"description":"Lesson id, as returned by /v1/catalog."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Lesson"}}}},"429":{"description":"Rate limit exceeded. Wait the number of seconds in `Retry-After`. Every response, including this one, carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`."},"404":{"description":"No such record. A miss is a 404, never a 200 with an empty body."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/changelog":{"get":{"tags":["public-api"],"summary":"List what shipped","description":"Every user-facing change, newest first. The same entries that render at https://codetrain.ai/changelog/ and inside the product.","operationId":"listChangelog","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Changelog"}}}},"429":{"description":"Rate limit exceeded. Wait the number of seconds in `Retry-After`. Every response, including this one, carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`."}}}},"/v1/roadmap":{"get":{"tags":["public-api"],"summary":"List roadmap items by stage","description":"What is shipped, being built, next and under consideration. A shipped item names the changelog entry that proves it; nothing else carries a date, because a date on unbuilt work is a guess. The same data as https://codetrain.ai/roadmap/.","operationId":"listRoadmap","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Roadmap"}}}},"429":{"description":"Rate limit exceeded. Wait the number of seconds in `Retry-After`. Every response, including this one, carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`."}}}},"/v1/referrals/{code}":{"get":{"tags":["public-api"],"summary":"Check a referral code","description":"Whether a Conductor Network referral code belongs to somebody. Answers 200 for both true and false on purpose, so a caller can tell 'checked, not real' from 'could not check'. Returns a bare boolean and never the owner's name or id, and is rate limited harder than the read endpoints because most codes are somebody's first name.","operationId":"checkReferralCode","parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string","description":"The referral code from a codetrain.ai/r/<code> link.","title":"Code"},"description":"The referral code from a codetrain.ai/r/<code> link."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReferralCheck"}}}},"429":{"description":"Rate limit exceeded. Wait the number of seconds in `Retry-After`. Every response, including this one, carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"Catalog":{"properties":{"courses":{"items":{"$ref":"#/components/schemas/Course"},"type":"array","title":"Courses","description":"Every course, live and announced, in rail order."}},"type":"object","required":["courses"],"title":"Catalog"},"Changelog":{"properties":{"entries":{"items":{"$ref":"#/components/schemas/ChangelogEntry"},"type":"array","title":"Entries","description":"Newest first. The same list as /changelog/."}},"type":"object","required":["entries"],"title":"Changelog"},"ChangelogEntry":{"properties":{"id":{"type":"string","title":"Id","description":"Stable entry id, formatted <slug>-<YYYY-MM-DD>."},"date":{"type":"string","title":"Date","description":"ISO date the change reached production."},"kind":{"type":"string","title":"Kind","description":"One of course, class, lesson, feature."},"title":{"type":"string","title":"Title","description":"Headline for the entry."},"blurb":{"type":"string","title":"Blurb","description":"One sentence summary."},"body":{"type":"string","title":"Body","description":"What a user can now do, in a paragraph."}},"type":"object","required":["id","date","kind","title","blurb","body"],"title":"ChangelogEntry"},"Course":{"properties":{"id":{"type":"string","title":"Id","description":"Stable course id, usable at /v1/courses/{course_id}."},"title":{"type":"string","title":"Title","description":"Course title."},"blurb":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Blurb","description":"One sentence on what the course covers."},"tagline":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tagline","description":"Short subject list shown under the title."},"color":{"type":"string","title":"Color","description":"Hex colour this course uses on the course map."},"status":{"type":"string","title":"Status","description":"\"live\" when it can be started, \"soon\" when it is announced only."},"classes":{"items":{"$ref":"#/components/schemas/Klass"},"type":"array","title":"Classes","description":"Classes in teaching order."},"classes_total":{"type":"integer","title":"Classes Total","description":"Count of classes, live and soon."},"classes_live":{"type":"integer","title":"Classes Live","description":"Count of classes that can be started."},"lessons_live":{"type":"integer","title":"Lessons Live","description":"Count of startable lessons across the whole course."}},"type":"object","required":["id","title","color","status","classes","classes_total","classes_live","lessons_live"],"title":"Course"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"Health":{"properties":{"status":{"type":"string","title":"Status","description":"\"ok\" when the service is answering."},"version":{"type":"string","title":"Version","description":"API version serving this response."}},"type":"object","required":["status","version"],"title":"Health"},"Klass":{"properties":{"id":{"type":"string","title":"Id","description":"Stable class id, unique within its course."},"name":{"type":"string","title":"Name","description":"Class name."},"blurb":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Blurb","description":"One sentence on what the class covers."},"status":{"type":"string","title":"Status","description":"\"live\" when it can be started, \"soon\" when it is syllabus only."},"lessons":{"items":{"$ref":"#/components/schemas/Lesson"},"type":"array","title":"Lessons","description":"Startable lessons, in teaching order."},"soon":{"items":{"type":"string"},"type":"array","title":"Soon","description":"Topics named in the syllabus that are not built yet."},"lessons_live":{"type":"integer","title":"Lessons Live","description":"Count of startable lessons."},"lessons_total":{"type":"integer","title":"Lessons Total","description":"Startable lessons plus the ones still to come."},"exercises":{"type":"integer","title":"Exercises","description":"Total steps across the startable lessons."}},"type":"object","required":["id","name","status","lessons","soon","lessons_live","lessons_total","exercises"],"title":"Klass"},"Lesson":{"properties":{"id":{"type":"string","title":"Id","description":"Stable lesson id, usable at /v1/lessons/{lesson_id}."},"title":{"type":"string","title":"Title","description":"Lesson title as shown in the product."},"blurb":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Blurb","description":"One sentence on what the lesson covers."},"lang":{"type":"string","title":"Lang","description":"Runtime the lesson runs in, for example python or javascript."},"steps":{"type":"integer","title":"Steps","description":"How many steps the learner works through. Never their content."}},"type":"object","required":["id","title","lang","steps"],"title":"Lesson"},"ReferralCheck":{"properties":{"valid":{"type":"boolean","title":"Valid","description":"Whether the code belongs to somebody. Deliberately the only field: never the owner's name or id."}},"type":"object","required":["valid"],"title":"ReferralCheck"},"Roadmap":{"properties":{"stages":{"items":{"$ref":"#/components/schemas/RoadmapStage"},"type":"array","title":"Stages","description":"Stages in order, earliest first. A stage with no items is omitted."}},"type":"object","required":["stages"],"title":"Roadmap"},"RoadmapItem":{"properties":{"title":{"type":"string","title":"Title","description":"What the item is."},"body":{"type":"string","title":"Body","description":"A paragraph on why it matters."},"theme":{"type":"string","title":"Theme","description":"Which part of the product it belongs to."},"stage":{"type":"string","title":"Stage","description":"One of shipped, building, next, exploring."},"shipped":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Shipped","description":"For a shipped item, the changelog entry id that proves it. Absent otherwise."}},"type":"object","required":["title","body","theme","stage"],"title":"RoadmapItem"},"RoadmapStage":{"properties":{"key":{"type":"string","title":"Key","description":"Stage key: shipped, building, next or exploring."},"label":{"type":"string","title":"Label","description":"Human label for the stage."},"note":{"type":"string","title":"Note","description":"What this stage means, in one sentence."},"items":{"items":{"$ref":"#/components/schemas/RoadmapItem"},"type":"array","title":"Items","description":"Items at this stage."}},"type":"object","required":["key","label","note","items"],"title":"RoadmapStage"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}},"tags":[{"name":"public-api","description":"Read-only endpoints. No key, no account.","externalDocs":{"description":"Developer portal","url":"https://codetrain.ai/developers/"}},{"name":"health","description":"Liveness checks for uptime monitors."}],"servers":[{"url":"https://api.codetrain.ai","description":"Production"}],"externalDocs":{"description":"CodeTrain developer portal","url":"https://codetrain.ai/developers/"}}