Training

Courses, syllabi, enrollments and training records. A course is trained under Part 61 or Part 141; the shape is identical and only the enforcement differs. A published syllabus version is immutable , revising it makes a new version, and an enrollment stays pinned to the one it started under. A student's standing against each requirement is a ledger: entries are only ever appended, and a correction is a reversing entry rather than an edit, so hours flown are always traceable to the lesson that earned them.

40 endpoints · base URL https://api.aerscheduler.com

get/training/candidates

Which lesson is this booking?

The lessons a booking could be closing out, unfinished ones first, filtered to what the booking type can be , a dual booking offers flight lessons, ground offers ground. A rental or a guest flight offers nothing.

Query parameters

orgUserIdintegerThe student.
typestringThe booking type: dual, solo, ground or sim. Omit for every lesson.
limitintegerRows to return, 1,1000. Defaults to 1000; a larger value is clamped rather than rejected.
offsetintegerRows to skip, for paging. Defaults to 0.
sortstringField to order by before paging, as a dot path into the row , total, user.firstName. Omit to keep the endpoint's own order. Numbers and ISO timestamps order as numbers and instants, not as text, and empty values always sort last regardless of direction. Ordering happens before the page is cut, so it orders the whole collection rather than the page you are holding.
order"asc" | "desc"asc (default) or desc. Only meaningful with sort.

Responses

200OKOne entry per enrollment. Each lesson carries its ACS tasks (id, name, acsCode, standard), which are what taskGrades on a saved record refer to; complete is true only for a signed passing grade (a U is not complete); recordId is the unsigned draft for that lesson, if there is one, so a close-out can update it rather than creating a second row; draft is that row's grade, notes, hours, task marks and reservationId (the booking already linked, if any) so the close-out can show what was already written rather than overwriting it, and so it can refuse to Sign a draft that belongs to a different flight; gradedReservationIds are the bookings that already have a signed record on this enrollment; and gradedOn names the lesson signed on each of those bookings, so a reload of the close-out can still say this flight was graded.{ data: object[] }
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl https://api.aerscheduler.com/training/candidates \
  -H "Authorization: Bearer $AERSCHEDULER_KEY"
get/training/courses

List courses

The courses your school teaches. Requires the configureTraining grant or the admin role , a syllabus is not students' business.

Query parameters

includeArchivedbooleanInclude retired courses.
limitintegerRows to return, 1,1000. Defaults to 1000; a larger value is clamped rather than rejected.
offsetintegerRows to skip, for paging. Defaults to 0.
sortstringField to order by before paging, as a dot path into the row , total, user.firstName. Omit to keep the endpoint's own order. Numbers and ISO timestamps order as numbers and instants, not as text, and empty values always sort last regardless of direction. Ordering happens before the page is cut, so it orders the whole collection rather than the page you are holding.
order"asc" | "desc"asc (default) or desc. Only meaningful with sort.

Responses

200OKSuccess.{ data: object[] }
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl https://api.aerscheduler.com/training/courses \
  -H "Authorization: Bearer $AERSCHEDULER_KEY"
post/training/courses

Create a course

Creates the course and its first editable draft version, Rev A. Requires the configureTraining grant or the admin role.

Request body

The course.

namerequiredstring
descriptionstring
enrollmentFeeCentsintegerWhat the school charges to enroll, in cents. Omit for a free course.
enrollmentFeeLabelstringHow the fee reads on the invoice line.
regulatoryPart"part61" | "part141"Which regime this course is trained under. Defaults to part61. Cannot be changed later , a school moving to Part 141 creates a new course.
certificateSoughtstringprivate | instrument | commercial | cfi | atp | endorsement | other.
ratingIdintegerAn instruction rate to price this course from. Pricing only , a rating is a rate card, not the course.
targetDaysintegerHow many days the school plans this course to take. Feeds the pace on a student's record: with no target, nobody is ever behind. Omit for no plan.

Responses

200OKSuccess.{ data: object }
400Bad RequestValidation failed.
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl -X POST https://api.aerscheduler.com/training/courses \
  -H "Authorization: Bearer $AERSCHEDULER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"…"}'
post/training/credits

Post credit by hand

For transferred training and backfills. Measured in hours or a count, never both, and never negative , to take credit back, reverse the entry that granted it. A manual credit must carry a note. Lesson hours are posted when the instructor signs, not through this endpoint. Requires the manageEnrollment grant or the admin role.

Request body

The entry.

enrollmentIdrequiredinteger
requirementIdrequiredinteger
deciHoursintegerTenths.
countinteger
sourcerequired"transfer_141" | "transfer_61" | "simulator" | "manual"
notesstringRequired for manual.
occurredAtstringWhen the training actually happened. Required for transferred hours (transfer_61, transfer_141): recency is judged on this date, not on when the row was written. Defaults to now for simulator and manual credits.

Responses

200OKSuccess.{ data: object }
400Bad RequestThe entry would be meaningless or unexplained, a transfer is missing when it was flown, or the source is a signed lesson (those are posted by signing).
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
409The enrollment is closed.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl -X POST https://api.aerscheduler.com/training/credits \
  -H "Authorization: Bearer $AERSCHEDULER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"enrollmentId":1,"requirementId":1,"source":"transfer_141"}'
get/training/endorsements

List endorsements

A pilot's signed endorsements. Readable by the person they belong to and by instructors and admins , a member always sees only their own, whatever they ask for. Superseded ones are hidden unless asked for: a renewed 90-day solo replaces the earlier one, and listing both invites signing against the wrong row.

Query parameters

orgUserIdintegerWhose. Ignored for callers who are not staff.
includeSupersededbooleanInclude replaced endorsements.
limitintegerRows to return, 1,1000. Defaults to 1000; a larger value is clamped rather than rejected.
offsetintegerRows to skip, for paging. Defaults to 0.
sortstringField to order by before paging, as a dot path into the row , total, user.firstName. Omit to keep the endpoint's own order. Numbers and ISO timestamps order as numbers and instants, not as text, and empty values always sort last regardless of direction. Ordering happens before the page is cut, so it orders the whole collection rather than the page you are holding.
order"asc" | "desc"asc (default) or desc. Only meaningful with sort.

Responses

200OKSuccess.{ data: object[] }
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl https://api.aerscheduler.com/training/endorsements \
  -H "Authorization: Bearer $AERSCHEDULER_KEY"
post/training/endorsements

Sign an endorsement

The renderedText you send is stored verbatim and never regenerated. AC 61-65K replaced AC 61-65H and will itself be replaced; an endorsement has to keep saying what was signed on the day it was signed. Pass supersedesId to replace an earlier one , a renewed 90-day solo is a new endorsement, not an edit. Requires the instructor or admin role.

Request body

The endorsement.

orgUserIdrequiredintegerWho it is for.
templateCodestringAC 61-65K paragraph, e.g. A.3. Optional; recorded for reference.
titlestring
renderedTextrequiredstringThe endorsement exactly as signed, with every blank filled in.
expiresAtstringDefaults from the template , 90 days for a solo endorsement. Omit for one that does not expire.
enrollmentIdintegerOptional. Plenty of endorsements belong to a person rather than a course.
signerCertificateNumberstring
signerCertificateExpirystring
supersedesIdinteger

Responses

200OKSuccess.{ data: object }
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
409That endorsement has already been replaced.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl -X POST https://api.aerscheduler.com/training/endorsements \
  -H "Authorization: Bearer $AERSCHEDULER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"orgUserId":1,"renderedText":"…"}'
get/training/enrollments

List enrollments

Staff see everyone's. A student always sees only their own, whatever they ask for , §141.101 gives them the right to their record, and no right to anyone else's.

Query parameters

orgUserIdintegerFilter to one student. Ignored for callers who are not staff.
courseIdintegerFilter to one course.
statusstringenrolled | graduated | terminated | transferred
limitintegerRows to return, 1,1000. Defaults to 1000; a larger value is clamped rather than rejected.
offsetintegerRows to skip, for paging. Defaults to 0.
sortstringField to order by before paging, as a dot path into the row , total, user.firstName. Omit to keep the endpoint's own order. Numbers and ISO timestamps order as numbers and instants, not as text, and empty values always sort last regardless of direction. Ordering happens before the page is cut, so it orders the whole collection rather than the page you are holding.
order"asc" | "desc"asc (default) or desc. Only meaningful with sort.

Responses

200OKSuccess.{ data: object[] }
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl https://api.aerscheduler.com/training/enrollments \
  -H "Authorization: Bearer $AERSCHEDULER_KEY"
post/training/enrollments

Enroll a student

Pins the student to this syllabus version. A Part 141 course must be published first. Requires the manageEnrollment grant or the admin role.

Request body

The enrollment.

versionIdrequiredinteger
orgUserIdrequiredinteger
enrollmentCertificateNumberstring§141.93 certificate of enrollment.

Responses

200OKSuccess.{ data: object }
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
409Already enrolled, or the version is a draft, retired, or the course or member is archived.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl -X POST https://api.aerscheduler.com/training/enrollments \
  -H "Authorization: Bearer $AERSCHEDULER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"versionId":1,"orgUserId":1}'
get/training/grants

List training grants

Every grant in the school. Requires the admin role.

Query parameters

limitintegerRows to return, 1,1000. Defaults to 1000; a larger value is clamped rather than rejected.
offsetintegerRows to skip, for paging. Defaults to 0.
sortstringField to order by before paging, as a dot path into the row , total, user.firstName. Omit to keep the endpoint's own order. Numbers and ISO timestamps order as numbers and instants, not as text, and empty values always sort last regardless of direction. Ordering happens before the page is cut, so it orders the whole collection rather than the page you are holding.
order"asc" | "desc"asc (default) or desc. Only meaningful with sort.

Responses

200OKSuccess.{ data: object[] }
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl https://api.aerscheduler.com/training/grants \
  -H "Authorization: Bearer $AERSCHEDULER_KEY"
post/training/grants

Grant a training permission

Gives someone a training grant. courseId is only accepted for checkInstructor, which §141.37 designates per approved course. Requires the admin role.

Request body

The grant.

orgUserIdrequiredinteger
grantrequired"configureTraining" | "manageEnrollment" | "checkInstructor" | "auditor"
courseIdintegerOnly for checkInstructor. Omit for a school-wide designation.

Responses

200OKSuccess.{ data: object }
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl -X POST https://api.aerscheduler.com/training/grants \
  -H "Authorization: Bearer $AERSCHEDULER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"orgUserId":1,"grant":"configureTraining"}'
post/training/records

Grade a lesson

Creates or updates a training record. Editable until it is signed and frozen the moment it is. A passing grade on a lesson the syllabus marked sign-off optional also signs and posts credits (signed: true on the response), because the console then treats the lesson as complete and hides Sign. Pass reservationId to tie it to the booking it came from. Requires the instructor or admin role.

Request body

The grade.

enrollmentIdrequiredinteger
lessonIdrequiredinteger
recordIdintegerOmit to create.
gradestringFrom the course version's grading scale. Defaults to S / U / I.
notesstring
flightDeciHoursinteger | nullTenths. Normally the Hobbs delta. Omit on an update to leave the existing figure; send null to clear it.
instructionDeciHoursinteger | nullTenths. Normally the briefing time. Omit on an update to leave the existing figure; send null to clear it.
simulatorDeciHoursinteger | nullTenths. The part flown in a simulator or FTD. Omit on an update to leave the existing figure; send null to clear it.
reservationIdinteger
instructorOrgUserIdintegerDefaults to the caller.
occurredAtstringWhen the training happened. The phone sends this from a grade queued offline so recency is judged on the flying, not on the sync. A linked booking's start still wins. Defaults to now.
taskGradesobject[]

Responses

200OKSaved. warning carries a Part 141 out-of-order notice, if any. signed is true when a passing grade on a sign-off-optional lesson also posted its credits.{ data: object }
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
409The record is already signed.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl -X POST https://api.aerscheduler.com/training/records \
  -H "Authorization: Bearer $AERSCHEDULER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"enrollmentId":1,"lessonId":1}'
get/training/templates

List syllabus templates

Ready-made syllabi a school can fork. Requires the configureTraining grant or the admin role.

Query parameters

limitintegerRows to return, 1,1000. Defaults to 1000; a larger value is clamped rather than rejected.
offsetintegerRows to skip, for paging. Defaults to 0.
sortstringField to order by before paging, as a dot path into the row , total, user.firstName. Omit to keep the endpoint's own order. Numbers and ISO timestamps order as numbers and instants, not as text, and empty values always sort last regardless of direction. Ordering happens before the page is cut, so it orders the whole collection rather than the page you are holding.
order"asc" | "desc"asc (default) or desc. Only meaningful with sort.

Responses

200OKSuccess.{ data: object[] }
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl https://api.aerscheduler.com/training/templates \
  -H "Authorization: Bearer $AERSCHEDULER_KEY"
get/training/courses/{courseId}

Get a course

The course and every version of its syllabus. Requires the configureTraining grant or the admin role.

Path parameters

courseIdrequiredintegerThe course id.

Responses

200OKSuccess.{ data: object }
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl https://api.aerscheduler.com/training/courses/:courseId \
  -H "Authorization: Bearer $AERSCHEDULER_KEY"
patch/training/courses/{courseId}

Update a course

Requires the configureTraining grant or the admin role. regulatoryPart cannot be changed.

Path parameters

courseIdrequiredintegerThe course id.

Request body

Fields to change.

namestring
descriptionstring
certificateSoughtstring
ratingIdinteger
targetDaysintegerHow many days the school plans this course to take, or null for no plan.
archivedbooleanArchive the course. It stops appearing on the Training page and cannot be enrolled on; existing students are untouched. Reversible.

Responses

200OKSuccess.{ data: object }
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl -X PATCH https://api.aerscheduler.com/training/courses/:courseId \
  -H "Authorization: Bearer $AERSCHEDULER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"…","description":"…"}'
get/training/endorsements/templates

Endorsement templates

The AC 61-65K Appendix A endorsements a Part 61/141 flight school actually signs. Pass orgUserId and the student's name is substituted for you; every other {placeholder} is left for the instructor, because inventing values would be putting words into a signature.

Query parameters

orgUserIdintegerPre-fill the student's name.
limitintegerRows to return, 1,1000. Defaults to 1000; a larger value is clamped rather than rejected.
offsetintegerRows to skip, for paging. Defaults to 0.
sortstringField to order by before paging, as a dot path into the row , total, user.firstName. Omit to keep the endpoint's own order. Numbers and ISO timestamps order as numbers and instants, not as text, and empty values always sort last regardless of direction. Ordering happens before the page is cut, so it orders the whole collection rather than the page you are holding.
order"asc" | "desc"asc (default) or desc. Only meaningful with sort.

Responses

200OKSuccess.{ data: object[] }
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl https://api.aerscheduler.com/training/endorsements/templates \
  -H "Authorization: Bearer $AERSCHEDULER_KEY"
get/training/enrollments/{enrollmentId}

A student's progress

The syllabus, every graded record against it, and where the ledger stands on each requirement , including what a simulator or transfer ceiling disallowed. Readable by the student themselves, instructors, admins, and anyone granted manageEnrollment.

Path parameters

enrollmentIdrequiredintegerThe enrollment id.

Responses

200OKSuccess.{ data: object }
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl https://api.aerscheduler.com/training/enrollments/:enrollmentId \
  -H "Authorization: Bearer $AERSCHEDULER_KEY"
delete/training/grants/{grantId}

Revoke a training grant

Requires the admin role.

Path parameters

grantIdrequiredintegerThe grant id.

Responses

200OKSuccess.{ data: object }
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl -X DELETE https://api.aerscheduler.com/training/grants/:grantId \
  -H "Authorization: Bearer $AERSCHEDULER_KEY"
get/training/grants/catalog

List the training grants on offer

The four grants a school can hand out, with a description of each. Any member may read this , it is a vocabulary, not a permission.

Query parameters

limitintegerRows to return, 1,1000. Defaults to 1000; a larger value is clamped rather than rejected.
offsetintegerRows to skip, for paging. Defaults to 0.
sortstringField to order by before paging, as a dot path into the row , total, user.firstName. Omit to keep the endpoint's own order. Numbers and ISO timestamps order as numbers and instants, not as text, and empty values always sort last regardless of direction. Ordering happens before the page is cut, so it orders the whole collection rather than the page you are holding.
order"asc" | "desc"asc (default) or desc. Only meaningful with sort.

Responses

200OKSuccess.{ data: object[] }
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl https://api.aerscheduler.com/training/grants/catalog \
  -H "Authorization: Bearer $AERSCHEDULER_KEY"
get/training/grants/mine

What the caller may do in training

The grants held by the caller, the ones their role implies, and whether they may grade. Every client needs this to decide which buttons to offer.

Responses

200OKSuccess.{ data: object }
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl https://api.aerscheduler.com/training/grants/mine \
  -H "Authorization: Bearer $AERSCHEDULER_KEY"
post/training/templates/{templateKey}

Create a course from a template

Builds a complete course , stages, lessons, ACS-coded tasks and requirements, with the credit map already wired , as an editable draft. Templates are always created as Part 61: a template cannot be an FAA-approved Training Course Outline, because approval is per school and per FSDO. Requires the configureTraining grant or the admin role.

Path parameters

templateKeyrequiredintegerThe template key, e.g. ppl-asel-part61.

Request body

Optional overrides.

namestringDefaults to the template's name.
ratingIdinteger

Responses

200OKSuccess.{ data: object }
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
404Not FoundNo such template.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl -X POST https://api.aerscheduler.com/training/templates/:templateKey \
  -H "Authorization: Bearer $AERSCHEDULER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"…","ratingId":1}'
get/training/versions/{versionId}

Get a syllabus version

The whole syllabus: stages, lessons, tasks and requirements. Requires the configureTraining grant or the admin role.

Path parameters

versionIdrequiredintegerThe version id.

Responses

200OKSuccess.{ data: object }
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl https://api.aerscheduler.com/training/versions/:versionId \
  -H "Authorization: Bearer $AERSCHEDULER_KEY"
post/training/courses/{courseId}/versions

Create a syllabus version

Pass copyFromVersionId to fork an existing version , the normal way to revise a published syllabus, since the published one cannot be edited. The copy is deep and the new version is an editable draft. Requires the configureTraining grant or the admin role.

Path parameters

courseIdrequiredintegerThe course id.

Request body

The version.

labelrequiredstringFree text, e.g. "Rev B" , schools match what they filed with their FSDO.
copyFromVersionIdinteger

Responses

200OKSuccess.{ data: object }
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
409A version with that label already exists.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl -X POST https://api.aerscheduler.com/training/courses/:courseId/versions \
  -H "Authorization: Bearer $AERSCHEDULER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"label":"…"}'
post/training/credits/{creditId}/reverse

Reverse a ledger entry

Writes a negative entry alongside. Nothing is deleted, and a reversal cannot itself be reversed. Hours posted by a signed lesson are taken back by amending the lesson, not here. Requires the manageEnrollment grant or the admin role.

Path parameters

creditIdrequiredintegerThe entry id.

Request body

Why.

reasonrequiredstring

Responses

200OKSuccess.{ data: object }
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
409Already reversed, itself a reversal, or the hours came from a signed lesson.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl -X POST https://api.aerscheduler.com/training/credits/:creditId/reverse \
  -H "Authorization: Bearer $AERSCHEDULER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"reason":"…"}'
post/training/enrollments/{enrollmentId}/certify

Certify a training record

§141.85 , the chief instructor certifying the record before graduation. Requires the checkInstructor designation for this course, or the admin role. The record stays amendable after certify.

Path parameters

enrollmentIdrequiredintegerThe enrollment id.

Responses

200OKSuccess.{ data: object }
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
409The enrollment is closed, or the record is already certified.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl -X POST https://api.aerscheduler.com/training/enrollments/:enrollmentId/certify \
  -H "Authorization: Bearer $AERSCHEDULER_KEY"
post/training/enrollments/{enrollmentId}/end

Terminate or transfer an enrollment

§141.101 wants the date and the reason. Requires the manageEnrollment grant or the admin role.

Path parameters

enrollmentIdrequiredintegerThe enrollment id.

Request body

How it ended.

statusrequired"terminated" | "transferred"
reasonstring

Responses

200OKSuccess.{ data: object }
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
409The enrollment has already ended.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl -X POST https://api.aerscheduler.com/training/enrollments/:enrollmentId/end \
  -H "Authorization: Bearer $AERSCHEDULER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status":"terminated"}'
post/training/enrollments/{enrollmentId}/fee

Bill the course enrollment fee

Raises an invoice, or a ledger charge if the school bills that way, for the fee this enrollment snapshotted when the student enrolled. Refuses if the course had no fee or the fee has already been billed , a second charge for the same fee is not something a refund undoes cleanly. Requires the manageEnrollment grant or the admin role.

Path parameters

enrollmentIdrequiredintegerThe enrollment id.

Responses

200OKSuccess.{ data: object }
400Bad RequestStripe or billing configuration refused the charge.
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
409There is no fee on this enrollment, or it has already been billed.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl -X POST https://api.aerscheduler.com/training/enrollments/:enrollmentId/fee \
  -H "Authorization: Bearer $AERSCHEDULER_KEY"
post/training/enrollments/{enrollmentId}/graduate

Graduate a student

Under Part 141 this refuses while any FAA-sourced requirement is unmet, and requires the chief instructor to have certified the record. Under Part 61 nothing blocks , the instructor decides readiness. Requires the manageEnrollment grant or the admin role.

Path parameters

enrollmentIdrequiredintegerThe enrollment id.

Request body

Optional.

graduationCertificateNumberstring§141.95 graduation certificate.

Responses

200OKSuccess.{ data: object }
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
409Requirements are unmet, or the enrollment has already ended.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl -X POST https://api.aerscheduler.com/training/enrollments/:enrollmentId/graduate \
  -H "Authorization: Bearer $AERSCHEDULER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"graduationCertificateNumber":"…"}'
post/training/records/{recordId}/amend

Amend a signed lesson

A signed record is never edited. This writes a new record superseding it and reverses everything the original posted to the ledger. Both records stay, and so do both sets of ledger entries. Requires the instructor or admin role.

Path parameters

recordIdrequiredintegerThe record id.

Request body

Why.

reasonrequiredstringStays on the record and on every reversing entry.

Responses

200OKSuccess.{ data: object }
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
409Not signed, or already amended.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl -X POST https://api.aerscheduler.com/training/records/:recordId/amend \
  -H "Authorization: Bearer $AERSCHEDULER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"reason":"…"}'
post/training/records/{recordId}/countersign

Countersign a lesson

The student's signature on what their instructor recorded. Only the student on the record may call it, and only after the instructor has signed.

Path parameters

recordIdrequiredintegerThe record id.

Responses

200OKSuccess.{ data: object }
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenNot your record.
409The instructor has not signed yet, or this lesson does not ask for a student signature.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl -X POST https://api.aerscheduler.com/training/records/:recordId/countersign \
  -H "Authorization: Bearer $AERSCHEDULER_KEY"
post/training/records/{recordId}/sign

Sign a lesson

Freezes the record and posts what it earned to the ledger, atomically. By default the credits come from what the syllabus says the lesson credits; pass credits to override , weather turns a night cross-country into a local day flight, and the record has to be able to say so. A second Sign on an already-signed record is a no-op (200 with the existing creditsPosted), so save-then-sign after a sign-off-optional save does not fail. Requires the instructor or admin role.

Path parameters

recordIdrequiredintegerThe record id.

Request body

Optional credit override.

creditsobject[]
occurredAtstringWhen the training happened. Same field as grade. A linked booking's start still wins.

Responses

200OKSigned. creditsPosted is how many ledger entries it wrote.{ data: object }
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
409Not graded yet, or the enrollment is closed.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl -X POST https://api.aerscheduler.com/training/records/:recordId/sign \
  -H "Authorization: Bearer $AERSCHEDULER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"credits":[],"occurredAt":"2026-08-04T15:00:00-06:00"}'
put/training/versions/{versionId}/gradingScale

Set the grading scale

Declares this course's marks and which of them mean a lesson is complete. "3" passes at a school marking 1,4 and fails at one marking 1,3, so guessing from the string is how a gradebook quietly disagrees with the school using it. Draft versions only. Requires the configureTraining grant or the admin role.

The scale is written as rows and read back as two fields. Anywhere a course version is returned, gradingScale is the list of codes in display order and gradeOptions is the same list with each mark's passing flag. Build a grade picker from gradingScale, and read gradeOptions when you need to know what a mark means.

Path parameters

versionIdrequiredintegerThe version id.

Request body

The full scale, in display order.

scalerequiredobject[]

Responses

200OKSuccess.{ data: object }
400Bad RequestEvery grade cannot fail, and two grades cannot share a name.
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
409The version is published, or training records exist on it.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl -X PUT https://api.aerscheduler.com/training/versions/:versionId/gradingScale \
  -H "Authorization: Bearer $AERSCHEDULER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"scale":[]}'
put/training/versions/{versionId}/lessons

Create or update a lesson

Omit lessonId to create. credits declares what this lesson is designed to credit and from which clock , this is what makes one flight post to several requirements at once. Only works on a draft. Requires the configureTraining grant or the admin role.

Path parameters

versionIdrequiredintegerThe version id.

Request body

The lesson.

lessonIdinteger
stageIdrequiredinteger
namerequiredstring
positionrequiredinteger
kindrequired"ground" | "flight" | "sim"
objectivesstring
completionStandardsstring
minFlightDeciHoursintegerTenths of an hour.
minGroundDeciHoursintegerTenths of an hour.
requiresSignoffbooleanMust an instructor sign this lesson before it counts? Defaults true.
requiresNotesbooleanMust the instructor write something before signing? Defaults false.
isStageCheckbooleanIs this the stage's §141.37 stage check? Signing one requires the checkInstructor designation for this course, and the signer must not be the student's own instructor. Defaults false.
creditsobject[]Which requirements this lesson credits.

Responses

200OKSuccess.{ data: object }
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
409The version is published, or training records exist and the credit map, sign-off, notes, or stage-check flag would change.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl -X PUT https://api.aerscheduler.com/training/versions/:versionId/lessons \
  -H "Authorization: Bearer $AERSCHEDULER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"stageId":1,"name":"…","position":1,"kind":"ground"}'
post/training/versions/{versionId}/publish

Publish a syllabus version

Freezes the version permanently. After this, no stage, lesson, task or requirement under it can be changed , students are enrolled against exactly these lessons. To revise, create a new version from this one. Requires the configureTraining grant or the admin role.

Path parameters

versionIdrequiredintegerThe version id.

Request body

Optional FSDO approval details, for Part 141.

approvedAtstring
approvalReferencestring

Responses

200OKSuccess.{ data: object }
400Bad RequestThe version has no lessons.
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
409Already published.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl -X POST https://api.aerscheduler.com/training/versions/:versionId/publish \
  -H "Authorization: Bearer $AERSCHEDULER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"approvedAt":"2026-08-04T15:00:00-06:00","approvalReference":"…"}'
put/training/versions/{versionId}/requirements

Create or update a requirement

What a student must accumulate , 35 hours total, 3 hours night, 10 towered landings. Measured in hours or a count, never both. source is part61, part141 or school; only FAA-sourced ones can block a Part 141 graduation. Requires the configureTraining grant or the admin role.

Path parameters

versionIdrequiredintegerThe version id.

Request body

The requirement.

requirementIdinteger
coderequiredstringStable machine key, e.g. "total_flight", "night", "cross_country".
labelrequiredstring
minDeciHoursintegerTenths of an hour.
minCountinteger
source"part61" | "part141" | "school"
maxSimulatorBpsintegerCeiling on simulator credit, in basis points. Appendix B allows 2000 (20%).
maxTransferBpsintegerCeiling on transferred credit, in basis points. §141.77 allows 5000 from an approved course, 2500 otherwise.
recencyCalendarMonthsintegerTraining older than this many CALENDAR months stops counting toward this requirement. §61.109(a)(4) wants the three hours of test preparation within 2. Omit or send null for a requirement with no window , but note that omitting it on an update CLEARS an existing window.

Responses

200OKSuccess.{ data: object }
400Bad RequestMeasured in hours or events, never both, and never neither.
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
409The version is published, or the code is already used.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl -X PUT https://api.aerscheduler.com/training/versions/:versionId/requirements \
  -H "Authorization: Bearer $AERSCHEDULER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"code":"…","label":"…"}'
post/training/versions/{versionId}/retire

Retire a syllabus version

Stops new enrollments. Students already on it finish on it. Requires the configureTraining grant or the admin role.

Path parameters

versionIdrequiredintegerThe version id.

Request body

Set retired false to un-retire.

retiredboolean

Responses

200OKSuccess.{ data: object }
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl -X POST https://api.aerscheduler.com/training/versions/:versionId/retire \
  -H "Authorization: Bearer $AERSCHEDULER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"retired":true}'
put/training/versions/{versionId}/stages

Create or update a stage

Omit stageId to create. Only works on a draft. Requires the configureTraining grant or the admin role.

Path parameters

versionIdrequiredintegerThe version id.

Request body

The stage.

stageIdinteger
namerequiredstring
objectivestring
positionrequiredinteger
requiresStageCheckboolean

Responses

200OKSuccess.{ data: object }
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
409The version is published and cannot be changed.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl -X PUT https://api.aerscheduler.com/training/versions/:versionId/stages \
  -H "Authorization: Bearer $AERSCHEDULER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"…","position":1}'
delete/training/versions/{versionId}/lessons/{lessonId}

Delete a lesson

Only works on a draft. Requires the configureTraining grant or the admin role.

Path parameters

versionIdrequiredintegerThe version id.
lessonIdrequiredintegerThe lesson id.

Responses

200OKSuccess.{ data: object }
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
409The version is published, or training records exist on it.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl -X DELETE https://api.aerscheduler.com/training/versions/:versionId/lessons/:lessonId \
  -H "Authorization: Bearer $AERSCHEDULER_KEY"
delete/training/versions/{versionId}/requirements/{requirementId}

Delete a requirement

Only works on a draft. Requires the configureTraining grant or the admin role.

Path parameters

versionIdrequiredintegerThe version id.
requirementIdrequiredintegerThe requirement id.

Responses

200OKSuccess.{ data: object }
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
409The version is published, or training records exist on it.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl -X DELETE https://api.aerscheduler.com/training/versions/:versionId/requirements/:requirementId \
  -H "Authorization: Bearer $AERSCHEDULER_KEY"
delete/training/versions/{versionId}/stages/{stageId}

Delete a stage

Only works on a draft. Requires the configureTraining grant or the admin role.

Path parameters

versionIdrequiredintegerThe version id.
stageIdrequiredintegerThe stage id.

Responses

200OKSuccess.{ data: object }
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
409The version is published, or training records exist on it.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl -X DELETE https://api.aerscheduler.com/training/versions/:versionId/stages/:stageId \
  -H "Authorization: Bearer $AERSCHEDULER_KEY"
put/training/versions/{versionId}/lessons/{lessonId}/tasks

Replace a lesson's tasks

Send the full list; it replaces what is there. acsCode maps a task to the ACS, e.g. PA.VI.A. Requires the configureTraining grant or the admin role.

Path parameters

versionIdrequiredintegerThe version id.
lessonIdrequiredintegerThe lesson id.

Request body

The full task list.

tasksrequiredobject[]

Responses

200OKSuccess.{ data: object }
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
409The version is published, or training records exist on the tasks.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl -X PUT https://api.aerscheduler.com/training/versions/:versionId/lessons/:lessonId/tasks \
  -H "Authorization: Bearer $AERSCHEDULER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"tasks":[]}'