AerScheduler API
Book aircraft, close flights out, invoice them, and pull the numbers back. 280 endpoints, described in OpenAPI 3.1, included on every plan.
- Auth
- API key, no expiry by default
- Format
- JSON over HTTPS, OpenAPI 3.1
- Limits
- 300/min per account
- Version
- v1.0.0
Quickstart
One credential, then you’re working. An administrator creates a key in the console under Settings → API keys. The secret is shown once, right there, and every request after that just carries it. No sign-in step in your integration, and no token to refresh.
# An administrator creates it in the console:
# Settings → API keys → Create key
# The secret is shown once, right there.
export AERSCHEDULER_KEY=ask_live_…curl -s https://api.aerscheduler.com/resources/planes \
-H "Authorization: Bearer $AERSCHEDULER_KEY"
# → { "data": [ { "id": 12, "name": "N12345", … } ],
# "pagination": { "total": 6, "hasMore": false, … } }curl -s -X POST https://api.aerscheduler.com/reservations \
-H "Authorization: Bearer $AERSCHEDULER_KEY" \
-H 'Content-Type: application/json' \
-d '{
"title": "Discovery flight",
"type": "dual",
"start": "2026-08-04T15:00:00-06:00",
"end": "2026-08-04T17:00:00-06:00",
"timeZoneName": "America/Denver",
"resource": { "id": 12 }
}'Reference
280 endpoints across 27 areas. Click an endpoint for parameters, responses, and an example request.
The flight school itself: membership, invitations, and settings.
- get
/invitationsList pending invitations - get
/joinRequestsList join requests - get
/organizationsList your organizations - patch
/organizationsUpdate your organization - delete
/organizationsSchedule your organization for deletion - delete
/joinRequests/{id}Delete a join request - get
/organizations/{orgId}Get an organization - get
/organizations/adTrackingRead the Airworthiness Directive tracking mode and readiness - patch
/organizations/adTrackingSet the Airworthiness Directive tracking mode - post
/organizations/cancelDeletionCancel a scheduled organization deletion - get
/organizations/guestsList guests - post
/organizations/inviteInvite someone - get
/organizations/ledgerGet ledger billing mode - patch
/organizations/ledgerSet ledger billing mode - get
/organizations/ratingsList ratings - post
/organizations/ratingsCreate a rating - post
/joinRequests/{id}/acceptAccept a join request - post
/joinRequests/{id}/declineDecline a join request - post
/organizations/leave/{orgId}Leave an organization - get
/organizations/ledger/accountsList member account balances - patch
/organizations/ratings/{ratingId}Update a rating - delete
/organizations/ratings/{ratingId}Delete a rating - post
/organizations/removeUser/{userId}Remove a member
People and the roles they hold.
- get
/orgUsersList members - get
/usersList users - patch
/usersUpdate your own profile - delete
/usersDelete your own account - get
/orgUsers/{orgUserId}Get a member - get
/orgUsers/preferencesGet your notification preferences - patch
/orgUsers/preferencesUpdate your notification preferences - get
/users/{id}Get a user - patch
/users/mechanicCertificateSave your mechanic certificate - get
/users/smsGet your SMS opt-in status - get
/orgUsers/{orgUserId}/ledgerGet a member's ledger - get
/orgUsers/{orgUserId}/usersGet the person behind a membership - get
/users/{userId}/approvedResourcesList a member's approved resources - get
/users/{userId}/detailsGet a person's contact details - patch
/users/{userId}/detailsUpdate a person's contact details - get
/users/{userId}/emergencyContactsList a person's emergency contacts - post
/users/{userId}/emergencyContactsAdd an emergency contact - get
/users/{userId}/rolesGet a member's roles - patch
/users/{userId}/rolesSet a member's roles - post
/users/sms/opt-outOpt out of SMS - get
/orgUsers/{orgUserId}/ledger/auto-refillGet ledger auto-refill - patch
/orgUsers/{orgUserId}/ledger/auto-refillUpdate ledger auto-refill - post
/orgUsers/{orgUserId}/ledger/entriesPost an admin ledger credit or adjustment - get
/orgUsers/{orgUserId}/ledger/refundableList refundable top-ups - post
/orgUsers/{orgUserId}/ledger/refundsRefund from a member ledger - get
/orgUsers/{orgUserId}/ledger/statementGet a member ledger statement - post
/orgUsers/{orgUserId}/ledger/topupsStart a ledger card top-up - patch
/users/{userId}/emergencyContacts/{contactId}Update an emergency contact - delete
/users/{userId}/emergencyContacts/{contactId}Remove an emergency contact - patch
/users/{userId}/orgUser/archiveArchive or restore a member - post
/users/sms/verify/confirmConfirm an SMS verification code - post
/users/sms/verify/startSend an SMS verification code - post
/orgUsers/{orgUserId}/ledger/statement/emailEmail a member ledger statement - post
/orgUsers/{orgUserId}/ledger/topups/confirmConfirm a ledger card top-up - post
/orgUsers/{orgUserId}/ledger/entries/{entryId}/reassignReassign a flight charge - get
/orgUsers/{orgUserId}/ledger/entries/{entryId}/receiptGet a ledger charge receipt - post
/orgUsers/{orgUserId}/ledger/entries/{entryId}/reverseReverse a flight charge
Booking, rescheduling, and closing out flights.
- get
/reservationsList reservations - post
/reservationsCreate a reservation - get
/reservations/{id}Get a reservation - patch
/reservations/{id}Update a reservation - delete
/reservations/{id}Cancel a reservation - post
/reservations/{id}/confirmReviewSign off the close-out - post
/reservations/{id}/invoicesInvoice a reservation - put
/reservations/{id}/payersSet who pays what - post
/reservations/{id}/rampInRamp in - post
/reservations/{id}/rampOutRamp out - post
/reservations/{id}/reopenReopen a close-out - post
/reservations/{id}/updateReviewTimesCorrect recorded times - get
/reservations/resource/{resourceId}List a resource's reservations - get
/reservations/user/{userId}List a person's reservations - post
/reservations/{id}/prepaid/ensureSend the package invoice - post
/reservations/{id}/prepaid/record-offlineRecord a cash or check package payment
How a booking's cost divides when more than one person is on it. Each person is billed separately, so a shared booking produces one invoice per payer. An organization that sets no rules bills one person for the whole booking.
When people and aircraft are free.
Aircraft, simulators, and rooms.
- get
/resourcesList resources - post
/resourcesCreate a resource - get
/resources/{id}Get a resource - patch
/resources/{id}Update a resource - delete
/resources/{id}Delete a resource - get
/resources/aircraftList aircraft - get
/resources/roomsList rooms - get
/resources/simulatorsList simulators - post
/resources/{id}/approveApprove a member for a resource - get
/resources/{id}/approvedUsersList who is approved on a resource - get
/resources/{id}/filesList papers on an aircraft - post
/resources/{id}/filesAdd papers to an aircraft - post
/resources/{id}/fuelRecord fuel on board - patch
/resources/{id}/groundingGround an aircraft or return it to service - post
/resources/{id}/unapproveWithdraw a member's approval - patch
/resources/{id}/files/{fileId}Rename or recategorize a paper - delete
/resources/{id}/files/{fileId}Remove a paper from an aircraft
Who changed what, and when.
Squawks and maintenance reminders.
- get
/maintenance/complianceList compliance records - get
/maintenance/remindersList maintenance reminders - get
/maintenance/squawksList squawks - post
/maintenance/squawksReport a squawk - get
/maintenance/compliance/{complianceRecordId}Get a compliance record - get
/maintenance/reminders/{maintenanceReminderId}Get a maintenance reminder - post
/maintenance/reminders/{maintenanceReminderId}Sign off a maintenance reminder - get
/maintenance/reminders/presetsList inspection presets - get
/maintenance/reminders/templatesList reminder templates - post
/maintenance/reminders/templatesCreate a reminder template - get
/maintenance/squawks/{squawkId}Get a squawk - post
/maintenance/squawks/{squawkId}Act on a squawk - post
/maintenance/reminders/{maintenanceReminderId}/filesAttach files to an open inspection - delete
/maintenance/reminders/{maintenanceReminderId}/filesRemove a file from an open inspection - get
/maintenance/reminders/templates/{maintenanceReminderTemplateId}Get a reminder template - patch
/maintenance/reminders/templates/{maintenanceReminderTemplateId}Update a reminder template - delete
/maintenance/reminders/templates/{maintenanceReminderTemplateId}Delete a reminder template - get
/maintenance/resources/{resourceId}/squawksList an aircraft's squawks - post
/maintenance/squawks/{squawkId}/commentsAdd a note to a squawk
Billing for completed flights.
- get
/invoicesList invoices - post
/invoicesCreate an invoice - get
/invoices/{invoiceId}Get an invoice - patch
/invoices/{invoiceId}Update an invoice - get
/invoices/summarySummarise invoices - post
/invoices/{invoiceId}/remindSend a payment reminder - get
/invoices/orgUsers/{orgUserId}List a member's invoices - get
/invoices/reservation/{reservationId}Get a reservation's invoice - get
/invoices/reservation/{reservationId}/invoiceTotalsGet a reservation's totals
Bases of operation.
Sets of members or resources.
- get
/groups/orgUserList member groups - post
/groups/orgUserCreate a member group - get
/groups/resourceList resource groups - post
/groups/resourceCreate a resource group - get
/groups/orgUser/{orgUserGroupId}Get a member group - patch
/groups/orgUser/{orgUserGroupId}Update a member group - delete
/groups/orgUser/{orgUserGroupId}Delete a member group - get
/groups/resource/{resourceGroupId}Get a resource group - patch
/groups/resource/{resourceGroupId}Update a resource group - delete
/groups/resource/{resourceGroupId}Delete a resource group
Recurring requirements members must stay current on.
- get
/currenciesList my currencies - get
/currencies/{currencyId}Get a currency record - post
/currencies/{currencyId}Renew a currency - patch
/currencies/{currencyId}Update a currency record - get
/currencies/typesList currency types - post
/currencies/typesCreate a currency type - get
/currencies/types/{currencyTypeId}Get a currency type - patch
/currencies/types/{currencyTypeId}Update a currency type - delete
/currencies/types/{currencyTypeId}Delete a currency type - get
/currencies/types/{currencyTypeId}/currenciesList a type's currency records - get
/currencies/types/{currencyTypeId}/detailGet a currency rule detail
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.
- get
/training/candidatesWhich lesson is this booking? - get
/training/coursesList courses - post
/training/coursesCreate a course - post
/training/creditsPost credit by hand - get
/training/endorsementsList endorsements - post
/training/endorsementsSign an endorsement - get
/training/enrollmentsList enrollments - post
/training/enrollmentsEnroll a student - get
/training/grantsList training grants - post
/training/grantsGrant a training permission - post
/training/recordsGrade a lesson - get
/training/templatesList syllabus templates - get
/training/courses/{courseId}Get a course - patch
/training/courses/{courseId}Update a course - get
/training/endorsements/templatesEndorsement templates - get
/training/enrollments/{enrollmentId}A student's progress - delete
/training/grants/{grantId}Revoke a training grant - get
/training/grants/catalogList the training grants on offer - get
/training/grants/mineWhat the caller may do in training - post
/training/templates/{templateKey}Create a course from a template - get
/training/versions/{versionId}Get a syllabus version - post
/training/courses/{courseId}/versionsCreate a syllabus version - post
/training/credits/{creditId}/reverseReverse a ledger entry - post
/training/enrollments/{enrollmentId}/certifyCertify a training record - post
/training/enrollments/{enrollmentId}/endTerminate or transfer an enrollment - post
/training/enrollments/{enrollmentId}/feeBill the course enrollment fee - post
/training/enrollments/{enrollmentId}/graduateGraduate a student - post
/training/records/{recordId}/amendAmend a signed lesson - post
/training/records/{recordId}/countersignCountersign a lesson - post
/training/records/{recordId}/signSign a lesson - put
/training/versions/{versionId}/gradingScaleSet the grading scale - put
/training/versions/{versionId}/lessonsCreate or update a lesson - post
/training/versions/{versionId}/publishPublish a syllabus version - put
/training/versions/{versionId}/requirementsCreate or update a requirement - post
/training/versions/{versionId}/retireRetire a syllabus version - put
/training/versions/{versionId}/stagesCreate or update a stage - delete
/training/versions/{versionId}/lessons/{lessonId}Delete a lesson - delete
/training/versions/{versionId}/requirements/{requirementId}Delete a requirement - delete
/training/versions/{versionId}/stages/{stageId}Delete a stage - put
/training/versions/{versionId}/lessons/{lessonId}/tasksReplace a lesson's tasks
What a club or an FBO charges people for belonging, as opposed to for flying. A plan carries a one-time join fee, recurring dues, or both; a membership is one person's copy of a plan with the money snapshotted at the moment it started, so re-pricing a plan never re-prices anybody already on it. Every dues period is a ledger row, and a period is billed because the membership's own cursor says it is owed , never because of what day it happens to be. Dues and join fees are raised as ordinary invoices, so they appear under Invoices and in the revenue reports with everything else.
- get
/membershipsList memberships - post
/membershipsPut a member on a plan - get
/memberships/{membershipId}Get a membership - patch
/memberships/{membershipId}Update a membership - get
/memberships/meThe caller's own membership - get
/memberships/plansList membership plans - post
/memberships/plansCreate a membership plan - get
/memberships/vocabularyCadences and statuses - post
/memberships/{membershipId}/duesBill the next dues period - post
/memberships/{membershipId}/joinFeeBill the join fee - post
/memberships/{membershipId}/planMove a member to a different plan - post
/memberships/{membershipId}/statusStart, pause, resume or end a membership - get
/memberships/orgUser/{orgUserId}One member's membership - patch
/memberships/plans/{planId}Update a membership plan - get
/memberships/plans/optionsPlans a member may be put on - post
/memberships/{membershipId}/dues/skipWaive the next dues period - post
/memberships/plans/{planId}/archiveRetire a plan, or bring it back - get
/memberships/plans/{planId}/ratesA tier's aircraft rates - put
/memberships/plans/{planId}/rates/{resourceId}Set or clear a tier's rate for one aircraft
Files held against members.
- post
/userDocumentsUpload a document - get
/userDocuments/{documentId}Get a document - patch
/userDocuments/{documentId}Update a document - delete
/userDocuments/{documentId}Delete a document - get
/userDocuments/typesList document types - post
/userDocuments/typesCreate a document type - post
/userDocuments/{documentId}/signedUrlGet a signed URL for a document - get
/userDocuments/orgUsers/{orgUserId}List a member's documents - get
/userDocuments/types/{documentTypeId}Get a document type - patch
/userDocuments/types/{documentTypeId}Update a document type - delete
/userDocuments/types/{documentTypeId}Delete a document type
Notices to members.
Per-member in-app notifications.
Standby interest, cancellation recovery, and time-limited slot offers.
- get
/slot-offersList pending slot offers - post
/slot-offersCreate a slot offer - get
/standbyList organization standby interest - post
/standbyCreate standby interest - get
/slot-offers/meList my slot offers - delete
/standby/{id}Withdraw standby interest - get
/standby/meList my standby interest - post
/slot-offers/{id}/acceptAccept a slot offer - post
/slot-offers/{id}/declineDecline a slot offer - post
/slot-offers/{id}/withdrawWithdraw a slot offer
When org policy requires approval, members submit requests and desk staff approve or decline them before a reservation is created.
- get
/booking-requestsList pending booking requests - post
/booking-requestsSubmit a booking request - get
/booking-requests/{id}Get a booking request - get
/booking-requests/meList my booking requests - post
/booking-requests/{id}/approveApprove a booking request - post
/booking-requests/{id}/cancelCancel a booking request - post
/booking-requests/{id}/convertInvite a public guest as a member - post
/booking-requests/{id}/rejectReject a booking request - get
/booking-requests/policy/meCheck if I need approval
Presets over org calendar policy for guest pages and internal slot discovery, plus per-audience calendar visibility settings.
- get
/booking-offeringsList booking offerings - post
/booking-offeringsCreate a booking offering - get
/booking-offerings/{id}Get a booking offering - patch
/booking-offerings/{id}Update a booking offering - get
/booking-offerings/visibilityGet calendar visibility - patch
/booking-offerings/visibilityUpdate calendar visibility - get
/booking-offerings/{id}/slotsList offering slots
Unauthenticated guest request pages. A guest picks an offering slot, confirms by email, and the front desk approves or declines the request. Members keep booking as they do today.
- post
/public/book/confirmConfirm a public booking request - get
/public/book/{orgSlug}/offerings/{offeringSlug}Get a public offering page - post
/public/book/{orgSlug}/offerings/{offeringSlug}/requestsSubmit a public booking request - get
/public/book/{orgSlug}/offerings/{offeringSlug}/slotsList public offering slots
The reporting engine and saved views.
- get
/reports/catalogList available reports - post
/reports/exportExport a report as CSV - get
/reports/overviewOrganization overview - post
/reports/runRun a report - get
/reports/viewsList saved report views - post
/reports/viewsCreate a saved report view - patch
/reports/views/{viewId}Update a saved report view - delete
/reports/views/{viewId}Delete a saved report view
One search across the organization.
Field observations.
Health and status.
WebSocket tickets for live console updates (schedule, notifications, billing).
One compact read of what a member needs to see right now, sized for a home-screen widget or a wall display.
Conventions
These hold across the whole API, so you can write one client wrapper and stop thinking about it.
Responses are wrapped
A body-bearing success is { "data": … }. An error is { "message": "…" }, written to be safe to show a user. Three endpoints break this on purpose and say so: /health, the CSV export, and the OAuth redirects.
401 and 403 mean different things
401 means the token is dead. Get a new one. 403 means the token is fine and the answer is still no. Retrying a 403 will never help.
Your organization is implied
The token carries it. You never pass an organization id for your own org, and you can never read another organization’s records.
Money is in cents
Always an integer, never a float. A $165/hr wet rate is 16500.
Times carry an offset
Send start and end with an explicit UTC offset or Z. A bare local datetime is rejected rather than silently booked in the server’s zone. See below.
Parse leniently
New endpoints and new fields on existing responses ship without notice. Ignore fields you don’t recognise; anything that would break a working integration gets a new major version first.
Time zones, and why bookings are strict about them
Scheduling is anchored to the airport’s time zone, not the caller’s device. A booking made for 7am at the field is 7am at the field whether the person making it is in the office, at home, or in another state.
So start and end must carry an explicit UTC offset or Z. A bare 2026-08-04T15:00:00is rejected outright. It would be read in the server’s zone and silently book a different instant than the person picked, and nothing downstream would notice. Send timeZoneName (an IANA zone like America/Denver) alongside, so the booking records which zone it was made in.
Who can book what
Which reservation type a caller may create depends on the roles they hold. Roles are additive, so an instructor who is also a technician gets both sets.
| Role | May create |
|---|---|
| owner, admin, dispatcher | solo, dual, ground, guest, sim, rental, maintenance |
| instructor | solo, dual, ground, guest, sim |
| student | solo, dual, ground, sim |
| renter | rental |
| technician | maintenance |
Rate limits
Limits are applied per signed-in account, not per IP. A whole school behind one office address does not share one budget.
| Per account | 300 requests / minute, 5,000 / hour |
| Unauthenticated | 100 requests / 5 minutes, per IP |
| Unauthenticated endpoints | Tighter still, and limited per IP |
Every response carries RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset. A 429 carries Retry-After in seconds. Honour it rather than retrying on a fixed interval.
Pagination: what to know before you build
Every list is capped at 1,000 rows, whether or not you ask for a limit. Pass limit and offset to page:
GET /reservations?startDate=…&endDate=…&limit=100&offset=200Every list response carries a pagination object beside data:
{
"data": [ … ],
"pagination": {
"total": 7823,
"limit": 1000,
"offset": 0,
"returned": 1000,
"hasMore": true
}
}total: how many there are in all, before the page was taken.returned: how many are indataright now.hasMore: whether another page exists. Check it. Some collections are bigger than one page: our largest school has over 7,000 reservations.
Asking for more than 1,000 gives you 1,000 rather than an error. offset past the end returns an empty data and hasMore: false. Date-ranged endpoints still matter for anything that grows without bound: GET /reservations requires a window, and the reporting engine takes one too. Cursor pagination is planned and will be additive:limit/offset will keep working.
Common questions
- Does AerScheduler have an API?
- Yes. AerScheduler exposes a REST API covering 280 endpoints across scheduling, aircraft, maintenance, billing, members, and reporting.
- Is the API included in the price?
- Yes. API access is included on every plan at no extra cost. There is no enterprise tier and no per-call charge.
- How do I authenticate with the AerScheduler API?
- With an API key, and only with an API key. Create one in the web console under Settings → API keys, then send it as an Authorization: Bearer header on every request. The secret is shown once, when you create it, so store it then. Only a hash is kept.
- Who can create an API key?
- Any administrator, from Settings → API keys in the web console. That is deliberately not something a key can do. A key cannot create or revoke another key, even one holding the admin role, so a leaked key can never issue itself replacements.
- What can an API key do?
- Exactly what the roles you gave it allow. A key behaves like a member holding those roles, and every permission rule that applies to a person applies to it. Give a key the least it needs. A key that reads the schedule should be a dispatcher, not an admin. The owner role cannot be granted to a key.
- Do keys expire?
- Only if you set an expiry when you create one. Otherwise a key works until you revoke it, which takes effect on the very next request. Revoked keys stay listed as revoked rather than disappearing, so anything they created still has something to point at.
- Is there an OpenAPI specification?
- Yes. The machine-readable OpenAPI 3.1 document is published at https://api.aerscheduler.com/openapi.json and needs no authentication to read. Point any standard client generator at it to produce an SDK in your language.
- Are there rate limits?
- Yes, and they are applied per signed-in account rather than per IP, so a whole school behind one office address does not share one budget. 300 requests a minute and 5,000 an hour per account. Every response carries RateLimit-Remaining, and a 429 carries Retry-After.
- Can I use the API to build a booking page on my own website?
- Yes. Availability, resources, and reservations are all exposed, so you can check an aircraft's free windows and create a booking from your own front end.
Building something?
Tell us what you’re integrating and we’ll help. If an endpoint you need doesn’t exist yet, we’d rather hear it now than read about it later.