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 atokenquery parameter. Without it you get401. 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 ownread:reservations-only key. - Scope is enforced per endpoint; a valid key without the right scope gets
403and the codeINSUFFICIENT_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
Scopes
Which scope unlocks which endpoint
| Scope | Endpoints |
|---|---|
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 and the two things that surprise people — read:members.full not working on its own, and an empty scope list meaning read-all — are on the authentication section.
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
| Field | Type | Notes |
|---|---|---|
id | string | — |
aircraft_id | string | null | — |
pilot_id | string | null | — |
instructor_id | string | null | — |
activity_type | string | dual_training, solo, ground, checkride, flight_review, checkout |
start_time | string | null | ISO 8601, UTC |
end_time | string | null | ISO 8601, UTC |
status | string | confirmed, standby, completed, cancelled, no_show, expired |
checked_in_at | string | null | ISO 8601, UTC |
completed_at | string | null | ISO 8601, UTC |
{
"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. Note that the cursor filters on id while the rows are ordered by start_time descending, so paging with after alone will not walk the set in start-time order — for a reliable sweep, window with start_after / start_before and page inside each window.
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
| Field | Type | Notes |
|---|---|---|
id | string | — |
tail_number | string | — |
aircraft_type | string | — |
display_name | string | null | — |
is_active | boolean | false while grounded |
hobbs_current | number | null | — |
tach_current | number | null | — |
annual_due_date | string | null | ISO 8601 date |
last_100hr_hobbs | number | null | — |
[
{
"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
| Field | Type | Notes |
|---|---|---|
id | string | — |
aircraft_id | string | — |
description | string | — |
severity | string | minor, major, grounding |
status | string | open, in_progress, resolved, deferred |
is_grounding | boolean | — |
created_at | string | null | ISO 8601, UTC |
resolved_at | string | null | ISO 8601, UTC |
[
{
"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
| Field | Type | Notes |
|---|---|---|
user_id | string | — |
role_id | string | null | — |
role_name | string | null | — |
status | string | — |
is_discovery | boolean | — |
joined_at | string | null | ISO 8601, UTC |
email | string | read:members.full only |
first_name | string | read:members.full only |
last_name | string | read:members.full only |
phone | string | null | read:members.full only |
[
{
"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
| Field | Type | Notes |
|---|---|---|
id | string | — |
aircraft_id | string | — |
event_type | string | 100hr, annual, oil_change, ad, other |
event_date | string | null | ISO 8601 date |
hobbs_at_event | number | null | — |
tach_at_event | number | null | — |
ap_cert_number | string | null | — |
ia_cert_number | string | null | — |
ia_name | string | null | — |
ia_sign_off | string | null | — |
return_to_service | boolean | null | — |
performed_by_name | string | null | — |
performed_by_cert_number | string | null | — |
performed_at | string | null | ISO 8601, UTC |
last_100hr_hobbs | number | null | — |
annual_due_date | string | null | ISO 8601 date |
work_performed | string | truncated to the first 1000 characters |
[
{
"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
| Field | Type | Notes |
|---|---|---|
id | string | — |
aircraft_id | string | — |
title | string | null | — |
work_type | string | null | — |
status | string | open, in_progress, completed, cancelled |
ap_signer_id | string | null | — |
ia_signer_id | string | null | — |
created_at | string | null | ISO 8601, UTC |
completed_at | string | null | ISO 8601, UTC |
[
{
"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
Response fields
| Field | Type | Notes |
|---|---|---|
UID | string | hbs-resv-<reservation id>@hobbstack.com |
DTSTART / DTEND | string | UTC basic format, e.g. 20260806T140000Z |
SUMMARY | string | aircraft id followed by the activity type |
STATUS | string | the reservation status, upper-cased |
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:VCALENDARResult 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