Pilots
Pilots List (Dropdown)
GET /pilots/list.json
Retrieve a simplified list of all active, non-deleted pilots in the company. Useful for dropdowns and selection fields.
Response
{
"pilots": {
"123": "John Doe",
"124": "Jane Smith"
}
}Keyed by user ID → "name surname". Sorted by UserDetail.name, UserDetail.surname.
Pilots Index (Paginated)
GET /pilots/index/page:{page}/search:{search}/user_group_id:{groupId}/pilot_group:{groupId}/base_id:{baseId}/active:{active}/pilot:{pilot}/limit:{limit}.json
Retrieve a paginated, filterable list of pilots with full details. Must be a JSON or AJAX request unless excel:1 is set.
Path Parameters
All filter parameters are optional — use empty string to skip.
page
number
Page number (starts at 1)
search
string
Matches name, surname, name+surname, surname+name, companyid, passport, email prefix, or exact User.id
user_group_id
string
Filter by user group/role. 150 is treated as <= 150 (all managers)
pilot_group
string
Filter by pilot group ID
base_id
string
Filter by base
active
boolean
Filter active/inactive users (active:false to include only inactive)
pilot
boolean
Filter pilot vs non-pilot accounts
limit
number
Page size (default 50, max 10000)
excel
boolean
Render the XLS export view (limit forced to 100000)
Permissions & Field Visibility
Viewers with
user_group_id > 170(e.g. students) get a reduced field set and the result is restricted to users withuser_group_id <= 170(i.e. they cannot see other students/clients).Billing fields (
User.billing,UserBill,UserBillPackage) are only included when the company plan is notfreeand billing is enabled. They are flattened intoUserDetail.billing_balanceandUserDetail.package_balance.
Response
PilotGroup is reshaped to { color: name }. email_status reflects User.checkConfirmedEmail outcomes (e.g. confirmed, pending).
Create Pilot
POST /pilots/create.json
Create a new pilot account. Restricted to user_group_id ∈ {1, 100, 105, 110, 120, 150}.
The free plan is capped at 100 pilots — additional pilots return 400.
Request Body
User.user_group_iddefaults to190if omitted; values below150are rejected.User.company_idis forced from the session; do not send.UserDetail.timezone_iddefaults to the requesting user's timezone.If
User.emailis provided,email_statusis computed viaUser.checkConfirmedEmail. When the email is set, the user isactive, andsend_emailis true, anewstaffconfirmation mail is sent.UserDetail.nameandUserDetail.surnameare required.
Response
On failure: result = false, validation contains User.invalidFields().
My Pilot View
GET /pilots/view.json
Retrieve the authenticated user's own pilot profile. Same payload as View Pilot below.
Note: passing
userId = 3is treated as "view self" for legacy reasons.
View Pilot
GET /pilots/view/{id}.json
Retrieve full pilot details including certificates, pilot groups, attributed aircraft, and flight types. Scoped to the viewer's company.
Path Parameters
id
string
User ID
Permissions & Field Visibility
Viewer
user_group_id > 150viewing another pilot:User.email,User.user_group_id,User.user_login_count,User.email_status,User.expiration,UserDetail.phone, and the entireUserCertificatearray are stripped.Viewer
user_group_id > 150:UserDetail.notesandUserDetail.billing_balanceare always stripped.Viewer
user_group_id < 151: getsUserDetail.notes,address,pc,city,emergency_contact, latestUserLogin, and (when billing is enabled)billing_balance/package_balance.
Response
Returns 404 if the pilot is not in the viewer's company.
Edit Pilot
POST /pilots/edit.json
Update pilot profile details. Restricted to user_group_id ∈ {1, 100, 105, 110, 120, 150}.
Request Body
User.idis required.AttributedAircraft,FlightType, andPilotGroupare passed as{ id: truthy }maps; only the keys are used. Existing pilot ↔ aircraft / flight-type / pilot-group joins are wiped before re-saving.A
user_group_idchange to< 150requires the editor to haveuser_group_id <= 120.Self-edits cannot demote yourself away from
user_group_id = 100or below.When
emailchanges,email_statusis recomputed and aconfirmmail is sent if the user is active andsend_emailis true.
Response
On failure, errors is the flattened User.invalidFields().
Pilot Totals
GET /pilots/totals/{userId}.json
Cumulative flight hour totals (in seconds) for a pilot, broken down by function and rule.
Path Parameters
userId
string
User ID. Defaults to the authenticated user.
Response
other_companies aggregates time from other companies that share the same email. previous_time is UserDetail.flight_hours * 3600. All times are in seconds. flight_time = total_time − rules.SIM.time. PIC includes FI time.
Pilot Currency
GET /pilots/get_currency/{userId}/{d1}/{d2}/{d3}.json
Check pilot landings/hours within rolling day windows against the company-configured currency requirements.
Path Parameters
userId
string
User ID. Forced to the authenticated user when null or when caller user_group_id > 170.
d1
number
First window in days (default 30, max 999).
d2
number
Second window in days (default 90, max 999).
d3
number
Third window in days (default null/disabled).
Response
Hours are in seconds. requirements mirrors CompanySetting.currency{1,2,3}_{flighttime,landings}.
Pilot Time Limits
POST /pilots/get_time_limits.json
Monthly breakdown plus month/year totals of block flight time and duty time for a given user, anchored to a Unix timestamp.
Request Body
timestamp
number
Yes
Unix timestamp anchoring the month/year
user_id
number
Yes
Target user ID
Both fields must be numeric and non-empty.
Response
Flight totals are in hours (formatted strings). Duty totals come from PilotDutyRecord::__total. Daily values are summed by date.
Duty Records
GET /pilots/duty/{days}/{user}.json
POST /pilots/duty/{days}/{user}.json
GET — last N days of duty records
days
number
Number of trailing days to return (default 3)
user
string
Target user ID. Forced to self when caller user_group_id > 170 or null.
POST — bulk save duty records
Body is an array of PilotDutyRecord objects. Saved with saveMany(... atomic=false).
Certificates
Certificate Types
GET /pilots/certificate_types.json
Returns the canonical certificate type catalogue used across the app.
Response
type
string
Category — one of licence, rating, medical, training, document
validity
number | null
Default validity window in months. null = no expiry.
name
string
Display name
abbr
string
Short code for compact tables
icon
string
Font Awesome class
requires_expiration
boolean
Whether an expiration date should be required at input
requires_issue
boolean
Whether an issue date should be required at input
mandatory
boolean
Counts toward the valid certificate result
order
number
Display sort order
group_role
string
pilot | all — restricts which user roles see this type
List Certificates
GET /pilots/certificates/{userId}.json
Retrieve all certificates for a pilot plus the validity summary. JSON-only.
Response
Validity Check
valid is produced by UserCertificate::checkValidLicence($userGroupId, $UserCertificate):
result
boolean
Overall validity. true iff licence + rating + medical + training are all valid (or, for ground roles, just the relevant subset — see below).
licence
boolean | null
At least one valid licence cert.
rating
boolean | null
All rating certs valid (any single invalid rating fails).
medical
boolean | null
At least one valid medical cert.
training
boolean | null
All training certs valid.
limit
number | null
Earliest expiry timestamp (epoch seconds) across all certs with an expiration.
Special cases:
user_group_id == 200(student):resultonly requiresmedical.user_group_id == 300(admin / non-pilot):resultonly requireslicence.
View Certificate
GET /pilots/certificate/{certId}.json
Retrieve a single certificate with up to 5 attached uploads.
Add Certificate
POST /pilots/add_certificate.json
Create a new certificate. Uses multipart/form-data for the optional file attachment.
Request Body
For callers with
user_group_id > 150,user_idis forced to the authenticated user.For managers, the target user must belong to the same company.
issueandexpirationmust parse asY-m-dor they are silently dropped.If
photois present and uploads cleanly, anUploadrecord is created (typeisphoto/video/documentbased on MIME) and the file is sent to S3.
Response
errors is populated on validation failure or upload errors.
Delete Certificate
POST /pilots/delete_certificate/{id}.json
Delete a certificate. Callers with user_group_id > 150 can only delete their own certificates; managers can delete any certificate within their company.
Returns 404 if the certificate is not found in the caller's scope.
Manager-only Endpoints
Save Pilot Notes
POST /manager/pilots/notes.json
Persist a private (manager-only) note onto a pilot's UserDetail.notes. Requires user_group_id <= 170.
Request Body
pilot
string
Yes
Pilot user ID
notes
string
Yes
Note body
Delete Pilot
POST /manager/pilots/delete/{id}.json
Soft-delete a pilot. The caller cannot delete themselves.
Recalculate Duties
GET /manager/pilots/recalculate_duties/{date}.json
Re-runs PilotDutyRecord::autoCalc for every PIC/SIC/Supervisor on flights logged on {date} (format YYYY-MM-DD). Existing duty rows for that date are cleared first. Up to 100 flights are processed per call.
FI Assignments Tree
GET /manager/pilots/fi_assignments.json
Returns instructor → supervised-pilot trees for the company in a tree-view friendly shape (icon, color, href, text, nodes). Branches with more than 6 supervised pilots are flagged with backColor = "#ff9900".
For caller user_group_id <= 150, also returns unAssignedStudents — students whose supervisor_id is empty or points to an inactive instructor.
Audit
GET /manager/pilots/audit/from:{from}/to:{to}/group:{group}/pilot_group:{pilot_group}/pilot:{pilot}/all_certs:{all_certs}.json
Paginated audit view of pilots and instructors with their latest flight, training status, certificates, and supervisor.
Path Parameters
from
Lower bound on latest_flight_date (YYYY-MM-DD or epoch seconds)
to
Upper bound on latest_flight_date
group
user_group_id (or 150 for <= 150)
pilot
Truthy to restrict to pilots only
pilot_group
Pilot group ID
all_certs
If absent or false, certificates are filtered to licence, rating, medical only
JSON or AJAX requests return:
The HTML version of this endpoint also returns userGroups and pilotGroups for filter dropdowns.
Monthly Duties Report
GET /manager/pilots/duties/month:{month}/year:{year}/group:{group}/data:{data}.json
Per-pilot daily breakdown of duty / FDP / work time for a single month. Premium and unlimited plans only; lower plans return the upgrade view.
month
1–12 (default current month)
year
YYYY (default current year)
group
user_group_id (150 for <= 150)
data
duty (default), flight, or work — selects which in_*/out_* columns drive the totals
/manager/pilots/duties/{export}.json with a truthy export segment renders the XLS view instead.
Manager — Pilot Duty PDF
GET /manager/pilots/duty/{user}/{year}/{month}
Renders a duty/flight report as a PDF (not JSON). Listed here for completeness.
Notes on Removed Endpoints
POST /pilots/reminder.json (resend activation email) was removed. Use the standard email-confirmation flow triggered automatically by create / edit when send_email = true and the email changes or the account is newly activated.
Last updated