Reservations
Booking, rescheduling, and closing out flights.
16 endpoints · base URL https://api.aerscheduler.com
/reservationsList reservations
Bookings overlapping a date window. startDate and endDate are required , unlike every other list in this API, this one will not return an unbounded set.
Query parameters
startDate | string | Start of the window, ISO 8601. Include an offset or Z. |
endDate | string | End of the window, ISO 8601. |
q | string | Free-text filter. Matches the fields a person would search on. |
resourceId | string | Comma-separated resource ids to filter by. |
locationId | string | Comma-separated location ids to filter by. |
includeCanceled | boolean | Set true to include cancelled bookings. Defaults to false. |
ongoing | boolean | Set false to exclude bookings already in progress. Defaults to true. |
orderBy | "asc" | "desc" | Sort direction on start time. Defaults to desc. |
uninvoiced | boolean | Set true for only the bookings with no live Stripe invoice and no live ledger flight_charge. |
endedBefore | string | Only bookings that finished before this instant, ISO 8601. Note the date range is an *overlap* query , narrowing endDate will not exclude a flight that is still out, and this will. |
limit | integer | Rows to return, 1,1000. Defaults to 1000; a larger value is clamped rather than rejected. |
offset | integer | Rows to skip, for paging. Defaults to 0. |
sort | string | Field 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
200 | OK | The bookings.→ { data: Reservation[] } |
400 | Bad Request | The date range is missing or unparseable. |
401 | Unauthorized | No token, an expired token, or a malformed one. Sign in again. |
403 | Forbidden | Authenticated, but not allowed to do this. |
429 | Too Many Requests | Rate limited. Retry-After says how long to wait. |
curl https://api.aerscheduler.com/reservations \
-H "Authorization: Bearer $AERSCHEDULER_KEY"/reservationsCreate a reservation
Books a resource, a person, or both.
start and end must carry an explicit UTC offset or Z. A bare local datetime is rejected: it would be read in the server's zone and silently book a different instant than the customer picked.
Which type values you may create depends on your roles , see the Reservation types section. The server also refuses double-bookings and bookings on grounded aircraft.
Pass a recurrence object to create a repeating series. Occurrences are real reservations, so each one can be ramped, reviewed, and invoiced independently.
Request body
The booking.
titlerequired | string | Short label shown on the calendar. |
typerequired | "solo" | "shared" | "dual" | "ground" | "guest" | "sim" | "rental" | "maintenance" | What kind of booking. Restricted by the caller's roles , see the table above.
shared is several pilots with no instructor , two pilots splitting a cross-country, or a safety-pilot arrangement under 91.109. solo means ONE occupant (14 CFR 61.87) and takes exactly one person; an instructor aboard makes it a dual. |
startrequired | string | Start instant. Must carry an explicit offset or Z. |
endrequired | string | End instant. Must carry an explicit offset or Z. |
timeZoneNamerequired | string | IANA zone the booking is being made in , the field's zone, not the caller's device. |
notes | string | Free-text notes shown on the booking. |
resource | object | The aircraft, simulator, or room to book, as { id }. |
location | object | Where it happens, as { id }. Defaults to the resource's location. |
personnel | object | Who is on the booking. One person cannot fill two seats on the same booking.
Several people may share a side. How many depends on the booking type: a ground class takes up to 12 students, sim up to 6, dual and solo up to 4, and rental up to 4 renters. maintenance takes nobody and guest takes one guest. Exceeding a limit is a 400 naming the side and the number allowed.
Order matters. The first person on a side is the leading payer: they are billed when the organization's rule for a charge is "one person pays", and they take the remainder cent of an uneven division.
Everyone listed must be free for the slot and must meet the currency and grounding rules , the booking is refused naming whoever does not, not silently trimmed. |
recurrence | object | Optional. Creates a repeating series. |
collectionStyle | "close_out" | "prepaid_fixed" | When to collect. close_out (default) bills after the flight. prepaid_fixed invoices prepaidAmountCents when the reservation is created. Same style for desk calendar bookings and public offering approvals. Invoice-mode members and all guests get a Stripe invoice. Ledger-mode members get a ledger debit. Refused on recurring series and maintenance. Cancelling voids an unpaid invoice, refunds a paid one, or reverses the ledger debit. |
prepaidAmountCents | integer | null | Package price in cents. Required when collectionStyle is prepaid_fixed, at least 50. |
Responses
201 | Created | The booking that was created, or the series.→ { data: Reservation } |
400 | Bad Request | Rejected: a clash, a grounded aircraft, an ambiguous timestamp, or a type your roles may not create. |
401 | Unauthorized | No token, an expired token, or a malformed one. Sign in again. |
403 | Forbidden | Authenticated, but not allowed to do this. |
429 | Too Many Requests | Rate limited. Retry-After says how long to wait. |
curl -X POST https://api.aerscheduler.com/reservations \
-H "Authorization: Bearer $AERSCHEDULER_KEY" \
-H "Content-Type: application/json" \
-d '{"title":"Discovery flight","type":"solo","start":"2026-08-04T15:00:00-06:00","end":"2026-08-04T17:00:00-06:00","timeZoneName":"America/Denver"}'/reservations/{id}Get a reservation
One booking, with personnel and close-out figures. Invoice totals, Stripe pay links, and paymentOverrides follow the same rule as GET /invoices/{id}: a member does not receive another person's share or rate.
Path parameters
idrequired | integer | The reservation id. |
Responses
200 | OK | Success.→ { data: Reservation } |
401 | Unauthorized | No token, an expired token, or a malformed one. Sign in again. |
403 | Forbidden | Authenticated, but not allowed to do this. |
404 | Not Found | No such booking in your organization. |
429 | Too Many Requests | Rate limited. Retry-After says how long to wait. |
curl https://api.aerscheduler.com/reservations/:id \
-H "Authorization: Bearer $AERSCHEDULER_KEY"/reservations/{id}Update a reservation
Reschedules or edits a booking. The same clash and role rules as create apply.
Path parameters
idrequired | integer | The reservation id. |
Request body
Fields to change.
title | string | |
start | string | |
end | string | |
notes | string |
Responses
200 | OK | Success.→ { data: Reservation } |
400 | Bad Request | Rejected , usually a clash. |
401 | Unauthorized | No token, an expired token, or a malformed one. Sign in again. |
403 | Forbidden | Authenticated, but not allowed to do this. |
429 | Too Many Requests | Rate limited. Retry-After says how long to wait. |
curl -X PATCH https://api.aerscheduler.com/reservations/:id \
-H "Authorization: Bearer $AERSCHEDULER_KEY" \
-H "Content-Type: application/json" \
-d '{"title":"…","start":"2026-08-04T15:00:00-06:00"}'/reservations/{id}Cancel a reservation
Cancels rather than destroys: the booking stays readable with cancelledAt set, which is what keeps cancellation reporting honest. Both fields are required , cancellationCategory is what the report counts, cancellationReason is the detail that makes a count actionable. Get the current list of categories from GET /reports/cancellations/categories; sending a value that isn't on it is rejected.
409 RESERVATION_RAMPED_OUT once the aircraft has left: a flight that has flown cannot be cancelled, because ramp-in has already advanced the aircraft's own Hobbs and tach and cancelling cannot put them back. Void any invoice, reopen the close-out, and correct the readings instead.
Path parameters
idrequired | integer | The reservation id. |
Request body
Why it was cancelled.
reasonrequired | string | |
categoryrequired | string |
Responses
204 | No Content | Success. No body. |
400 | Bad Request | It is already cancelled, or too late to cancel under your organization's policy. |
401 | Unauthorized | No token, an expired token, or a malformed one. Sign in again. |
403 | Forbidden | Authenticated, but not allowed to do this. |
409 | RESERVATION_RAMPED_OUT: the aircraft has already left the ramp. Reopen the close-out and correct it instead. | |
429 | Too Many Requests | Rate limited. Retry-After says how long to wait. |
curl -X DELETE https://api.aerscheduler.com/reservations/:id \
-H "Authorization: Bearer $AERSCHEDULER_KEY" \
-H "Content-Type: application/json" \
-d '{"reason":"…","category":"…"}'/reservations/{id}/confirmReviewSign off the close-out
Confirms the recorded times. A dual flight needs both seats to confirm before it will invoice, which is why one person cannot occupy two seats on a booking.
Path parameters
idrequired | integer | The reservation id. |
Responses
200 | OK | Success.→ { data: Reservation } |
400 | Bad Request | Nothing to confirm yet. |
401 | Unauthorized | No token, an expired token, or a malformed one. Sign in again. |
403 | Forbidden | Authenticated, but not allowed to do this. |
429 | Too Many Requests | Rate limited. Retry-After says how long to wait. |
curl -X POST https://api.aerscheduler.com/reservations/:id/confirmReview \
-H "Authorization: Bearer $AERSCHEDULER_KEY"/reservations/{id}/invoicesInvoice a reservation
Generates the bill for a closed-out flight. Requires the admin role.
Returns an ARRAY. A booking shared between several people produces one invoice PER PAYER , a Stripe invoice bills exactly one customer, so splitting a booking's cost has to be several invoices rather than one invoice with shares underneath it. A booking with one payer returns an array of one.
How the cost divides is set per organization and per booking type , see the cost-splitting rules in the console. With no rules configured, one person is billed for the whole booking, which is how this endpoint has always behaved.
Partial success is possible and is not an error. If the payment processor accepts some invoices and rejects another, the ones that succeeded are real and are returned, with a warnings array naming who could not be billed. Call this endpoint again to bill only the payers who still have no invoice , it will not duplicate the ones already raised. A 400 means nothing was billed at all.
A share below the processor's 50-cent minimum is skipped rather than raised, and reported in warnings.
Path parameters
idrequired | integer | The reservation id. |
Responses
200 | OK | Success.→ { data: Invoice[] } |
201 | Created | Success.→ { data: Invoice[] } |
400 | Bad Request | The flight is not closed out, every payer is already invoiced, or the organization's split rules cannot be applied to it , for example a booking set to split by each person's own time where somebody's hours have not been entered. The message says what to fix. |
401 | Unauthorized | No token, an expired token, or a malformed one. Sign in again. |
403 | Forbidden | Authenticated, but not allowed to do this. |
429 | Too Many Requests | Rate limited. Retry-After says how long to wait. |
curl -X POST https://api.aerscheduler.com/reservations/:id/invoices \
-H "Authorization: Bearer $AERSCHEDULER_KEY"/reservations/{id}/payersSet who pays what
Records each person's stake in the cost of a booking: their own meter readings when the charge is split by the time each person flew, their percentage when it is split by set shares, or a waiver for somebody who is on the booking but owes nothing (a safety pilot).
Replaces the whole set. Shares have to total 100% across everybody, so merging one person into an existing set is how a total quietly stops adding up. Send everybody every time.
Only people already on the booking may be given a stake. Refused once the booking has been invoiced , the invoices describe the hours and shares they were computed from, and rewriting those underneath them would leave the money disagreeing with its own record.
Whether the shares total 100%, and whether the individual meter readings add up to what the aircraft actually ran, is checked when the booking is INVOICED rather than here , so a half-finished close-out can still be saved.
Path parameters
idrequired | integer | The reservation id. |
Request body
Everyone being billed for this booking.
payersrequired | object[] | One entry per person. Exactly one of orgUserId or guestId on each. |
Responses
200 | OK | Success.→ { data: Reservation } |
400 | Bad Request | A stake belongs to nobody, names somebody who isn't on the booking, has a reading that runs backwards, or the booking is already invoiced. The message says which. |
401 | Unauthorized | No token, an expired token, or a malformed one. Sign in again. |
403 | Forbidden | You are not on this booking and are not staff. |
429 | Too Many Requests | Rate limited. Retry-After says how long to wait. |
curl -X PUT https://api.aerscheduler.com/reservations/:id/payers \
-H "Authorization: Bearer $AERSCHEDULER_KEY" \
-H "Content-Type: application/json" \
-d '{"payers":[]}'/reservations/{id}/rampInRamp in
Records return meter readings and puts the aircraft back on the ramp.
Meter anomaly guard. A reading that looks like a typo, far more digits than the out reading, or hours flown wildly beyond what the booking could have covered, is rejected with 409 and code: "METER_ANOMALY" rather than silently accepted. The response's details.anomalies describes what looked wrong. Resubmit the same body with confirmMeterAnomaly: true once the desk has confirmed the reading is real.
Path parameters
idrequired | integer | The reservation id. |
Request body
Return readings.
hobbsTimeInrequired | number | |
tachTimeInrequired | number | |
confirmMeterAnomaly | boolean | Set true to accept a reading the anomaly guard flagged and resubmit despite it. |
Responses
200 | OK | Success.→ { data: Reservation } |
400 | Bad Request | Not ramped out, or you are not on this booking. |
401 | Unauthorized | No token, an expired token, or a malformed one. Sign in again. |
403 | Forbidden | Authenticated, but not allowed to do this. |
409 | The reading looks like a typo. See details.anomalies; resubmit with confirmMeterAnomaly: true to override. | |
429 | Too Many Requests | Rate limited. Retry-After says how long to wait. |
curl -X POST https://api.aerscheduler.com/reservations/:id/rampIn \
-H "Authorization: Bearer $AERSCHEDULER_KEY" \
-H "Content-Type: application/json" \
-d '{"hobbsTimeIn":1,"tachTimeIn":1}'/reservations/{id}/rampOutRamp out
Records departure meter readings and marks the aircraft off the ramp. The first step of closing a flight out.
Path parameters
idrequired | integer | The reservation id. |
Request body
Departure readings.
hobbsTimeOutrequired | number | |
tachTimeOutrequired | number |
Responses
200 | OK | Success.→ { data: Reservation } |
400 | Bad Request | The aircraft is already ramped out, or you are not on this booking. |
401 | Unauthorized | No token, an expired token, or a malformed one. Sign in again. |
403 | Forbidden | Authenticated, but not allowed to do this. |
429 | Too Many Requests | Rate limited. Retry-After says how long to wait. |
curl -X POST https://api.aerscheduler.com/reservations/:id/rampOut \
-H "Authorization: Bearer $AERSCHEDULER_KEY" \
-H "Content-Type: application/json" \
-d '{"hobbsTimeOut":1,"tachTimeOut":1}'/reservations/{id}/reopenReopen a close-out
Clears the sign-offs on a closed-out booking so its times, rates or cost split can be corrected, and the crew re-confirm afterwards.
Nothing else is undone: the readings, the crew and the booked times are left exactly as they are. This is the step that makes the existing correction endpoints reachable again on a booking everybody has already signed off.
Money comes first. A booking with a standing invoice, or an un-reversed ledger charge, is refused with 409 and code: "RESERVATION_BILLED". Void the invoice (or reverse the ledger charge) and call this again. A VOIDED invoice does not block it.
Narrower than correcting. A close-out PIN is a signature, so taking one back off a flight record is limited to the school's staff: an admin, a dispatcher, or the instructor on this booking. A pilot cannot clear another pilot's confirmation. Correcting a reading before anybody has signed needs no such authority. reason is required and is kept on the flight's audit record.
Path parameters
idrequired | integer | The reservation id. |
Request body
Why the close-out is being reopened.
reasonrequired | string | Kept on the audit record. Required. |
Responses
200 | OK | Success.→ { data: Reservation } |
400 | Bad Request | No reason given, or this booking was cancelled, or it has not been signed off yet. |
401 | Unauthorized | No token, an expired token, or a malformed one. Sign in again. |
403 | Forbidden | Only an admin, a dispatcher, or the instructor on this booking, can reopen a close-out. |
409 | Money is still standing against this booking. Void the invoice or reverse the ledger charge first. | |
429 | Too Many Requests | Rate limited. Retry-After says how long to wait. |
curl -X POST https://api.aerscheduler.com/reservations/:id/reopen \
-H "Authorization: Bearer $AERSCHEDULER_KEY" \
-H "Content-Type: application/json" \
-d '{"reason":"…"}'/reservations/{id}/updateReviewTimesCorrect recorded times
Rewrites a reading already recorded on a flight: Hobbs, tach, or the instruction time on a ground lesson. Only a figure the booking already holds can be corrected, so this opens once the aircraft is back and not before.
Every sign-off on the booking is cleared and the crew confirm the new figures. Clearing a sign-off that is not your own needs the same authority as reopening (an admin, a dispatcher, or the instructor on this booking); re-entering your own PIN afterwards does not. Refused with 409 RESERVATION_BILLED once the flight has been billed: void the invoice (or reverse the ledger charge) first.
A body that matches what is already stored writes nothing. Both first-party forms are prefilled, so an unedited Save is a full echo of the stored figures. That is answered 200 with noChanges: true, and no sign-off is touched.
The aircraft's own meters follow only when this flight is still what they read. Correcting an older booking leaves them alone rather than rolling the aircraft's logbook backwards; the response's meter.followed says which happened, and meter.aircraftHobbs / meter.aircraftTach report what the aircraft still reads so it can be corrected separately.
Two confirmable refusals. 409 METER_ANOMALY when the reading looks like a typo; 409 MAINTENANCE_TRIGGER when saving it would pass an hour-based inspection and ground the aircraft. Resubmit with confirmMeterAnomaly: true / confirmMaintenanceTrigger: true to accept.
Path parameters
idrequired | integer | The reservation id. |
Request body
The corrected figures. Send both halves of whichever meter you are correcting.
hobbsTimeOut | number | |
hobbsTimeIn | number | |
tachTimeOut | number | |
tachTimeIn | number | |
briefing | number | Instruction time, on a booking measured by it. |
confirmMeterAnomaly | boolean | |
confirmMaintenanceTrigger | boolean | Accept that saving this reading will ground the aircraft. |
Responses
200 | OK | The corrected booking, plus a report of what this call did to the aircraft.→ { data: Reservation } |
400 | Bad Request | Nothing to correct in the body, the booking holds no such figure, or it was cancelled. |
401 | Unauthorized | No token, an expired token, or a malformed one. Sign in again. |
403 | Forbidden | You are not on this booking, or it carries somebody else's sign-off and you are not an admin, a dispatcher, or its instructor. |
409 | One of three, told apart by code. METER_ANOMALY: the reading looks like a typo. MAINTENANCE_TRIGGER: saving it would ground the aircraft. Resubmit with the matching confirm flag for either. RESERVATION_BILLED: money still stands against the booking, so void the invoice or reverse the ledger charge first. | |
429 | Too Many Requests | Rate limited. Retry-After says how long to wait. |
curl -X POST https://api.aerscheduler.com/reservations/:id/updateReviewTimes \
-H "Authorization: Bearer $AERSCHEDULER_KEY" \
-H "Content-Type: application/json" \
-d '{"hobbsTimeOut":1,"hobbsTimeIn":1}'/reservations/resource/{resourceId}List a resource's reservations
Bookings for one aircraft, simulator, or room in a window.
Path parameters
resourceIdrequired | integer | The resource id. |
Query parameters
startDate | string | Start of the window, ISO 8601. Include an offset or Z. |
endDate | string | End of the window, ISO 8601. |
limit | integer | Rows to return, 1,1000. Defaults to 1000; a larger value is clamped rather than rejected. |
offset | integer | Rows to skip, for paging. Defaults to 0. |
sort | string | Field 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
200 | OK | The bookings.→ { data: Reservation[] } |
401 | Unauthorized | No token, an expired token, or a malformed one. Sign in again. |
403 | Forbidden | Authenticated, but not allowed to do this. |
429 | Too Many Requests | Rate limited. Retry-After says how long to wait. |
curl https://api.aerscheduler.com/reservations/resource/:resourceId \
-H "Authorization: Bearer $AERSCHEDULER_KEY"/reservations/user/{userId}List a person's reservations
Bookings a person is on, in a window.
Path parameters
userIdrequired | integer | The user id. |
Query parameters
startDate | string | Start of the window, ISO 8601. Include an offset or Z. |
endDate | string | End of the window, ISO 8601. |
limit | integer | Rows to return, 1,1000. Defaults to 1000; a larger value is clamped rather than rejected. |
offset | integer | Rows to skip, for paging. Defaults to 0. |
sort | string | Field 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
200 | OK | The bookings.→ { data: Reservation[] } |
401 | Unauthorized | No token, an expired token, or a malformed one. Sign in again. |
403 | Forbidden | Authenticated, but not allowed to do this. |
429 | Too Many Requests | Rate limited. Retry-After says how long to wait. |
curl https://api.aerscheduler.com/reservations/user/:userId \
-H "Authorization: Bearer $AERSCHEDULER_KEY"/reservations/{id}/prepaid/ensureSend the package invoice
Retries raising the Stripe package invoice (or ledger debit) on a prepaid_fixed booking that is already on the calendar. Does not cancel the booking if Stripe fails. Desk staff, the instructor on the booking, or the person who created it. Demo orgs are refused.
Path parameters
idrequired | integer | The reservation id. |
Responses
200 | OK | Success.→ { data: Reservation } |
400 | Bad Request | The package invoice could not be created. The booking is still on the calendar. |
401 | Unauthorized | No token, an expired token, or a malformed one. Sign in again. |
403 | Forbidden | You cannot collect payment on this booking. |
429 | Too Many Requests | Rate limited. Retry-After says how long to wait. |
curl -X POST https://api.aerscheduler.com/reservations/:id/prepaid/ensure \
-H "Authorization: Bearer $AERSCHEDULER_KEY"/reservations/{id}/prepaid/record-offlineRecord a cash or check package payment
Creates the package invoice if it is missing, then marks it paid out of band for cash or a check. Same people who may collect on the booking. Use this when the guest already paid at the desk, or when the package invoice never went out and they are paying by check or cash. Demo orgs are refused.
Path parameters
idrequired | integer | The reservation id. |
Request body
How they paid.
methodrequired | "cash" | "check" |
Responses
200 | OK | Success.→ { data: Reservation } |
400 | Bad Request | This booking is billed after the flight, or the invoice could not be marked paid. |
401 | Unauthorized | No token, an expired token, or a malformed one. Sign in again. |
403 | Forbidden | You cannot collect payment on this booking. |
429 | Too Many Requests | Rate limited. Retry-After says how long to wait. |
curl -X POST https://api.aerscheduler.com/reservations/:id/prepaid/record-offline \
-H "Authorization: Bearer $AERSCHEDULER_KEY" \
-H "Content-Type: application/json" \
-d '{"method":"cash"}'