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.

Parameter
Type
Description

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 with user_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 not free and billing is enabled. They are flattened into UserDetail.billing_balance and UserDetail.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_id defaults to 190 if omitted; values below 150 are rejected.

  • User.company_id is forced from the session; do not send.

  • UserDetail.timezone_id defaults to the requesting user's timezone.

  • If User.email is provided, email_status is computed via User.checkConfirmedEmail. When the email is set, the user is active, and send_email is true, a newstaff confirmation mail is sent.

  • UserDetail.name and UserDetail.surname are 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 = 3 is 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

Parameter
Type
Description

id

string

User ID

Permissions & Field Visibility

  • Viewer user_group_id > 150 viewing another pilot: User.email, User.user_group_id, User.user_login_count, User.email_status, User.expiration, UserDetail.phone, and the entire UserCertificate array are stripped.

  • Viewer user_group_id > 150: UserDetail.notes and UserDetail.billing_balance are always stripped.

  • Viewer user_group_id < 151: gets UserDetail.notes, address, pc, city, emergency_contact, latest UserLogin, 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.id is required.

  • AttributedAircraft, FlightType, and PilotGroup are 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_id change to < 150 requires the editor to have user_group_id <= 120.

  • Self-edits cannot demote yourself away from user_group_id = 100 or below.

  • When email changes, email_status is recomputed and a confirm mail is sent if the user is active and send_email is 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

Parameter
Type
Description

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

Parameter
Type
Description

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

Field
Type
Required
Description

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

Parameter
Type
Description

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

Field
Type
Description

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):

Field
Type
Description

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): result only requires medical.

  • user_group_id == 300 (admin / non-pilot): result only requires licence.

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_id is forced to the authenticated user.

  • For managers, the target user must belong to the same company.

  • issue and expiration must parse as Y-m-d or they are silently dropped.

  • If photo is present and uploads cleanly, an Upload record is created (type is photo/video/document based 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

Field
Type
Required
Description

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

Parameter
Description

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.

Parameter
Description

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