Skip to main content
Hobbstack

Developers / API reference

Every endpoint, every field

Seven read-only operations, scoped to one organization. Here is what each one returns, what it accepts, and where its result set stops.

Before you start

The rules that apply everywhere

  • Base URL is https://api.hobbstack.com. Every path below is complete — append nothing.
  • Authentication is Authorization: Bearer hbs_pk_… on every endpoint except the calendar feed, which takes the key as a token query parameter. Without it you get 401. That query parameter is the same full key, so a feed URL is a live credential for everything the key can read — give calendar feeds their own read:reservations-only key.
  • Scope is enforced per endpoint; a valid key without the right scope gets 403 and the code INSUFFICIENT_SCOPE.
  • Organization is taken from the key. There is no org parameter, and no key can read another organization’s records.
  • Timestamps are ISO 8601 in UTC. Date-only fields are YYYY-MM-DD.
  • Nothing here writes. There is no POST, PUT, PATCH or DELETE in the public API. A leaked key is a confidentiality problem, never an integrity one — revoke it in the app.

Response shapes are documented here, not in the OpenAPI document

The handlers build plain dictionaries rather than declaring response models, so the generated OpenAPI document describes every 200 as simply “Successful Response” with no schema. The field tables below were read out of the handlers themselves. If the two ever disagree, the running API is right — please tell us and we will fix the page.

Scopes

Which scope unlocks which endpoint

ScopeEndpoints
read:reservations/reservations /reservations.ics
read:aircraft/aircraft
read:squawks/squawks
read:members/members
read:members.full/members
read:maintenance/maintenance-logs /work-orders

Full descriptions are in the authentication section, along with the two rules that surprise people: read:members.full does nothing on its own, and every key needs at least one scope, because a key with none can’t read anything.

read:reservations

Reservations

GET /api/v1/external/reservations

Reservations for the key's organization, newest start time first, with optional filters and a cursor. Soft-deleted reservations are excluded.

Response fields

FieldTypeNotes
idstring—
aircraft_idstring | null—
pilot_idstring | null—
instructor_idstring | null—
activity_typestringdual_training, solo, ground, checkride, flight_review, checkout
start_timestring | nullISO 8601, UTC
end_timestring | nullISO 8601, UTC
statusstringconfirmed, standby, completed, cancelled, no_show, expired
checked_in_atstring | nullISO 8601, UTC
completed_atstring | nullISO 8601, UTC
Example response
json
{
  "items": [
    {
      "id": "6f2f1c9e-6a3b-4a1c-9a6a-2b1f3c4d5e6f",
      "aircraft_id": "b1c2d3e4-f5a6-4718-8293-a1b2c3d4e5f6",
      "pilot_id": "11111111-2222-4333-8444-555555555555",
      "instructor_id": null,
      "activity_type": "solo",
      "start_time": "2026-08-06T14:00:00+00:00",
      "end_time": "2026-08-06T16:00:00+00:00",
      "status": "confirmed",
      "checked_in_at": null,
      "completed_at": null
    }
  ],
  "next_cursor": null
}

Result set

This is the only paginated endpoint. limit defaults to 100 and caps at 500; next_cursor is non-null whenever a full page came back. Pass it back as after, with the same filters, to get the next page. Pages continue in the same order (newest start time first), so walking them returns every reservation once, with no gaps or repeats. A cursor that did not come from this organization's results is refused with 400 and the code INVALID_CURSOR.

read:aircraft

Aircraft

GET /api/v1/external/aircraft

Every aircraft in the organization that has not been soft-deleted. Returns a bare JSON array, not an envelope.

Response fields

FieldTypeNotes
idstring—
tail_numberstring—
aircraft_typestring—
display_namestring | null—
is_activebooleanfalse while grounded
hobbs_currentnumber | null—
tach_currentnumber | null—
annual_due_datestring | nullISO 8601 date
last_100hr_hobbsnumber | null—
Example response
json
[
  {
    "id": "b1c2d3e4-f5a6-4718-8293-a1b2c3d4e5f6",
    "tail_number": "N172SP",
    "aircraft_type": "C172S",
    "display_name": "172SP (G1000)",
    "is_active": true,
    "hobbs_current": 4821.6,
    "tach_current": 4390.2,
    "annual_due_date": "2027-03-31",
    "last_100hr_hobbs": 4780.0
  }
]

Result set

Unpaginated — the whole fleet comes back in one array.

read:squawks

Squawks

GET /api/v1/external/squawks

Reported discrepancies for the organization, newest first. Filter by status, aircraft, or whether the squawk grounds the aircraft.

Response fields

FieldTypeNotes
idstring—
aircraft_idstring—
descriptionstring—
severitystringminor, major, grounding
statusstringopen, in_progress, resolved, deferred
is_groundingboolean—
created_atstring | nullISO 8601, UTC
resolved_atstring | nullISO 8601, UTC
Example response
json
[
  {
    "id": "9c8b7a65-4321-4fed-8cba-0987654321fe",
    "aircraft_id": "b1c2d3e4-f5a6-4718-8293-a1b2c3d4e5f6",
    "description": "Left main tire worn to cord on outboard shoulder",
    "severity": "grounding",
    "status": "open",
    "is_grounding": true,
    "created_at": "2026-08-04T18:22:10+00:00",
    "resolved_at": null
  }
]

Result set

Hard-capped at the 500 most recent squawks with no cursor. An organization with more history will silently see only that window, so filter (status, aircraft_id, is_grounding) rather than expecting the full set.

read:members

Members

GET /api/v1/external/members

Membership records for the organization. Contact details appear only when the key also carries read:members.full.

Response fields

FieldTypeNotes
user_idstring—
role_idstring | null—
role_namestring | null—
statusstring—
is_discoveryboolean—
joined_atstring | nullISO 8601, UTC
emailstringread:members.full only
first_namestringread:members.full only
last_namestringread:members.full only
phonestring | nullread:members.full only
Example response
json
[
  {
    "user_id": "11111111-2222-4333-8444-555555555555",
    "role_id": "aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee",
    "role_name": "Member",
    "status": "active",
    "is_discovery": false,
    "joined_at": "2026-02-11T09:04:33+00:00"
  }
]

Result set

Unpaginated — every non-deleted membership in one array.

read:maintenance

Maintenance logs

GET /api/v1/external/maintenance-logs

FAR 43.9 maintenance record entries, most recent event date first. Built for audit exports.

Response fields

FieldTypeNotes
idstring—
aircraft_idstring—
event_typestring100hr, annual, oil_change, ad, other
event_datestring | nullISO 8601 date
hobbs_at_eventnumber | null—
tach_at_eventnumber | null—
ap_cert_numberstring | null—
ia_cert_numberstring | null—
ia_namestring | null—
ia_sign_offstring | null—
return_to_serviceboolean | null—
performed_by_namestring | null—
performed_by_cert_numberstring | null—
performed_atstring | nullISO 8601, UTC
last_100hr_hobbsnumber | null—
annual_due_datestring | nullISO 8601 date
work_performedstringtruncated to the first 1000 characters
Example response
json
[
  {
    "id": "3d3d3d3d-1111-4222-8333-444444444444",
    "aircraft_id": "b1c2d3e4-f5a6-4718-8293-a1b2c3d4e5f6",
    "event_type": "100hr",
    "event_date": "2026-07-28",
    "hobbs_at_event": 4780.0,
    "tach_at_event": 4351.4,
    "ap_cert_number": "3512345",
    "ia_cert_number": null,
    "ia_name": null,
    "ia_sign_off": null,
    "return_to_service": true,
    "performed_by_name": "R. Alvarez",
    "performed_by_cert_number": "3512345",
    "performed_at": "2026-07-28T22:10:00+00:00",
    "last_100hr_hobbs": 4780.0,
    "annual_due_date": "2027-03-31",
    "work_performed": "Performed 100-hour inspection per Cessna 172S maintenance manual..."
  }
]

Result set

Hard-capped at the 500 most recent entries with no cursor. Filter by aircraft_id for a complete per-aircraft history.

read:maintenance

Work orders

GET /api/v1/external/work-orders

Open and historical work orders for the organization, newest first.

Response fields

FieldTypeNotes
idstring—
aircraft_idstring—
titlestring | null—
work_typestring | null—
statusstringopen, in_progress, completed, cancelled
ap_signer_idstring | null—
ia_signer_idstring | null—
created_atstring | nullISO 8601, UTC
completed_atstring | nullISO 8601, UTC
Example response
json
[
  {
    "id": "7e7e7e7e-8888-4999-8aaa-bbbbbbbbbbbb",
    "aircraft_id": "b1c2d3e4-f5a6-4718-8293-a1b2c3d4e5f6",
    "title": "Replace left main tire",
    "work_type": "unscheduled",
    "status": "open",
    "ap_signer_id": null,
    "ia_signer_id": null,
    "created_at": "2026-08-04T18:40:02+00:00",
    "completed_at": null
  }
]

Result set

Hard-capped at the 500 most recent work orders with no cursor. Filter by status to narrow.

read:reservations

Reservation calendar feed

GET /api/v1/external/reservations.ics

The same reservations as an iCalendar feed, so a member can subscribe in Apple Calendar, Google Calendar or Outlook. Authenticated with the key in the URL because no calendar client sends an Authorization header on a subscription URL. Returns text/calendar, not JSON.

Mint a read:reservations-only key for calendar feeds

The token in this URL is a full API key, not a feed-only token. Anyone who has the URL has everything that key can read: member email addresses and phone numbers if it carries read:members.full, and every other endpoint its scopes allow. Create a dedicated key with read:reservations and nothing else for calendar subscriptions, and never paste a broad key into a calendar client. If a feed URL is ever shared or screenshotted, revoke that key rather than editing the subscription: the URL is already in the calendar provider's subscription record and in browser history, and revoking is the only thing that takes it out of service.

Response fields

FieldTypeNotes
UIDstringhbs-resv-<reservation id>@hobbstack.com
DTSTART / DTENDstringUTC basic format, e.g. 20260806T140000Z
SUMMARYstringaircraft id followed by the activity type
STATUSstringthe reservation status, upper-cased
Example response
ics
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Hobbstack//External API//EN
CALSCALE:GREGORIAN
BEGIN:VEVENT
UID:hbs-resv-6f2f1c9e-6a3b-4a1c-9a6a-2b1f3c4d5e6f@hobbstack.com
DTSTART:20260806T140000Z
DTEND:20260806T160000Z
SUMMARY:b1c2d3e4-f5a6-4718-8293-a1b2c3d4e5f6 solo
STATUS:CONFIRMED
END:VEVENT
END:VCALENDAR

Result set

The 500 most recent reservations by start time. Reservations missing a start or end time are skipped.

Want the machine-readable version?

The OpenAPI document is served without authentication, so any code generator can read it directly: https://api.hobbstack.com/api/v1/external/openapi.json