Reservations

Booking, rescheduling, and closing out flights.

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

get/reservations

List 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

startDatestringStart of the window, ISO 8601. Include an offset or Z.
endDatestringEnd of the window, ISO 8601.
qstringFree-text filter. Matches the fields a person would search on.
resourceIdstringComma-separated resource ids to filter by.
locationIdstringComma-separated location ids to filter by.
includeCanceledbooleanSet true to include cancelled bookings. Defaults to false.
ongoingbooleanSet false to exclude bookings already in progress. Defaults to true.
orderBy"asc" | "desc"Sort direction on start time. Defaults to desc.
uninvoicedbooleanSet true for only the bookings with no live Stripe invoice and no live ledger flight_charge.
endedBeforestringOnly 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.
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

200OKThe bookings.{ data: Reservation[] }
400Bad RequestThe date range is missing or unparseable.
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/reservations \
  -H "Authorization: Bearer $AERSCHEDULER_KEY"
post/reservations

Create 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.

titlerequiredstringShort 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.
startrequiredstringStart instant. Must carry an explicit offset or Z.
endrequiredstringEnd instant. Must carry an explicit offset or Z.
timeZoneNamerequiredstringIANA zone the booking is being made in , the field's zone, not the caller's device.
notesstringFree-text notes shown on the booking.
resourceobjectThe aircraft, simulator, or room to book, as { id }.
locationobjectWhere it happens, as { id }. Defaults to the resource's location.
personnelobjectWho 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.
recurrenceobjectOptional. 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.
prepaidAmountCentsinteger | nullPackage price in cents. Required when collectionStyle is prepaid_fixed, at least 50.

Responses

201CreatedThe booking that was created, or the series.{ data: Reservation }
400Bad RequestRejected: a clash, a grounded aircraft, an ambiguous timestamp, or a type your roles may not create.
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/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"}'
get/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

idrequiredintegerThe reservation id.

Responses

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

Update a reservation

Reschedules or edits a booking. The same clash and role rules as create apply.

Path parameters

idrequiredintegerThe reservation id.

Request body

Fields to change.

titlestring
startstring
endstring
notesstring

Responses

200OKSuccess.{ data: Reservation }
400Bad RequestRejected , usually a clash.
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/reservations/:id \
  -H "Authorization: Bearer $AERSCHEDULER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title":"…","start":"2026-08-04T15:00:00-06:00"}'
delete/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

idrequiredintegerThe reservation id.

Request body

Why it was cancelled.

reasonrequiredstring
categoryrequiredstring

Responses

204No ContentSuccess. No body.
400Bad RequestIt is already cancelled, or too late to cancel under your organization's policy.
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
409RESERVATION_RAMPED_OUT: the aircraft has already left the ramp. Reopen the close-out and correct it instead.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl -X DELETE https://api.aerscheduler.com/reservations/:id \
  -H "Authorization: Bearer $AERSCHEDULER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"reason":"…","category":"…"}'
post/reservations/{id}/confirmReview

Sign 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

idrequiredintegerThe reservation id.

Responses

200OKSuccess.{ data: Reservation }
400Bad RequestNothing to confirm yet.
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/reservations/:id/confirmReview \
  -H "Authorization: Bearer $AERSCHEDULER_KEY"
post/reservations/{id}/invoices

Invoice 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

idrequiredintegerThe reservation id.

Responses

200OKSuccess.{ data: Invoice[] }
201CreatedSuccess.{ data: Invoice[] }
400Bad RequestThe 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.
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/reservations/:id/invoices \
  -H "Authorization: Bearer $AERSCHEDULER_KEY"
put/reservations/{id}/payers

Set 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

idrequiredintegerThe reservation id.

Request body

Everyone being billed for this booking.

payersrequiredobject[]One entry per person. Exactly one of orgUserId or guestId on each.

Responses

200OKSuccess.{ data: Reservation }
400Bad RequestA 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.
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenYou are not on this booking and are not staff.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl -X PUT https://api.aerscheduler.com/reservations/:id/payers \
  -H "Authorization: Bearer $AERSCHEDULER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"payers":[]}'
post/reservations/{id}/rampIn

Ramp 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

idrequiredintegerThe reservation id.

Request body

Return readings.

hobbsTimeInrequirednumber
tachTimeInrequirednumber
confirmMeterAnomalybooleanSet true to accept a reading the anomaly guard flagged and resubmit despite it.

Responses

200OKSuccess.{ data: Reservation }
400Bad RequestNot ramped out, or you are not on this booking.
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenAuthenticated, but not allowed to do this.
409The reading looks like a typo. See details.anomalies; resubmit with confirmMeterAnomaly: true to override.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
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}'
post/reservations/{id}/rampOut

Ramp out

Records departure meter readings and marks the aircraft off the ramp. The first step of closing a flight out.

Path parameters

idrequiredintegerThe reservation id.

Request body

Departure readings.

hobbsTimeOutrequirednumber
tachTimeOutrequirednumber

Responses

200OKSuccess.{ data: Reservation }
400Bad RequestThe aircraft is already ramped out, or you are not on this booking.
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/reservations/:id/rampOut \
  -H "Authorization: Bearer $AERSCHEDULER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"hobbsTimeOut":1,"tachTimeOut":1}'
post/reservations/{id}/reopen

Reopen 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

idrequiredintegerThe reservation id.

Request body

Why the close-out is being reopened.

reasonrequiredstringKept on the audit record. Required.

Responses

200OKSuccess.{ data: Reservation }
400Bad RequestNo reason given, or this booking was cancelled, or it has not been signed off yet.
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenOnly an admin, a dispatcher, or the instructor on this booking, can reopen a close-out.
409Money is still standing against this booking. Void the invoice or reverse the ledger charge first.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl -X POST https://api.aerscheduler.com/reservations/:id/reopen \
  -H "Authorization: Bearer $AERSCHEDULER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"reason":"…"}'
post/reservations/{id}/updateReviewTimes

Correct 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

idrequiredintegerThe reservation id.

Request body

The corrected figures. Send both halves of whichever meter you are correcting.

hobbsTimeOutnumber
hobbsTimeInnumber
tachTimeOutnumber
tachTimeInnumber
briefingnumberInstruction time, on a booking measured by it.
confirmMeterAnomalyboolean
confirmMaintenanceTriggerbooleanAccept that saving this reading will ground the aircraft.

Responses

200OKThe corrected booking, plus a report of what this call did to the aircraft.{ data: Reservation }
400Bad RequestNothing to correct in the body, the booking holds no such figure, or it was cancelled.
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenYou are not on this booking, or it carries somebody else's sign-off and you are not an admin, a dispatcher, or its instructor.
409One 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.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
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}'
get/reservations/resource/{resourceId}

List a resource's reservations

Bookings for one aircraft, simulator, or room in a window.

Path parameters

resourceIdrequiredintegerThe resource id.

Query parameters

startDatestringStart of the window, ISO 8601. Include an offset or Z.
endDatestringEnd of the window, ISO 8601.
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

200OKThe bookings.{ data: Reservation[] }
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/reservations/resource/:resourceId \
  -H "Authorization: Bearer $AERSCHEDULER_KEY"
get/reservations/user/{userId}

List a person's reservations

Bookings a person is on, in a window.

Path parameters

userIdrequiredintegerThe user id.

Query parameters

startDatestringStart of the window, ISO 8601. Include an offset or Z.
endDatestringEnd of the window, ISO 8601.
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

200OKThe bookings.{ data: Reservation[] }
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/reservations/user/:userId \
  -H "Authorization: Bearer $AERSCHEDULER_KEY"
post/reservations/{id}/prepaid/ensure

Send 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

idrequiredintegerThe reservation id.

Responses

200OKSuccess.{ data: Reservation }
400Bad RequestThe package invoice could not be created. The booking is still on the calendar.
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenYou cannot collect payment on this booking.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
curl -X POST https://api.aerscheduler.com/reservations/:id/prepaid/ensure \
  -H "Authorization: Bearer $AERSCHEDULER_KEY"
post/reservations/{id}/prepaid/record-offline

Record 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

idrequiredintegerThe reservation id.

Request body

How they paid.

methodrequired"cash" | "check"

Responses

200OKSuccess.{ data: Reservation }
400Bad RequestThis booking is billed after the flight, or the invoice could not be marked paid.
401UnauthorizedNo token, an expired token, or a malformed one. Sign in again.
403ForbiddenYou cannot collect payment on this booking.
429Too Many RequestsRate limited. Retry-After says how long to wait.
Example request
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"}'