{
  "openapi": "3.1.0",
  "info": {
    "title": "AerScheduler API",
    "version": "1.0.0",
    "summary": "Schedule aircraft, close out flights, and bill for them — from your own software.",
    "description": "The AerScheduler REST API. Everything the mobile app and the web console can do, your own software can do too — schedule aircraft, close flights out, invoice them, and pull the numbers back.\n\nThis is the same API our own clients use. There is no separate \"public\" tier and no second implementation to fall behind.\n\n## Getting started\n\n```bash\n# 1. Sign in. The token is good for 30 days.\ncurl -s -X POST https://api.aerscheduler.com/auth \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"email\":\"you@yourschool.com\",\"password\":\"…\"}'\n\n# 2. Use it.\ncurl -s https://api.aerscheduler.com/resources/planes \\\n  -H \"Authorization: Bearer $TOKEN\"\n```\n\n## Conventions\n\nEvery one of these holds across the whole API, so you can write one client wrapper and stop thinking about it:\n\n- **Responses are wrapped.** A body-bearing success is `{ \"data\": … }`. An error is `{ \"message\": \"…\" }`, and the message is written to be safe to show a user. A few endpoints noted individually break this: `/health`, the CSV export, and the OAuth redirects.\n- **Status codes mean what they say.** `200` read or update, `201` created, `204` done with nothing to return, `400` your request was wrong, `401` sign in again, `403` signed in but not allowed, `404` no such thing, `429` slow down.\n- **`401` and `403` are different.** `401` means the token is dead — get a new one. `403` means the token is fine and the answer is still no; retrying will not help.\n- **Collections are `GET /thing`, items are `GET /thing/{id}`.** Create is `POST` to the collection, update is `PATCH` to the item, delete is `DELETE` on the item. Where a resource deviates, the endpoint says so.\n- **Your organization is implied.** The token carries it. You never pass an organization id, and you can never read another organization's records.\n- **Ids are integers** and stable for the life of the record.\n- **Money is in cents**, as an integer. Never a float.\n\n## Time zones\n\nScheduling is anchored to the **airport's** time zone, not the caller's device. A booking made at 7am at the field is 7am at the field regardless of where the person making it happens to be.\n\nIn practice that means: send `start` and `end` with an **explicit UTC offset or `Z`**. A bare local datetime like `2026-08-04T15:00:00` is rejected — it would be read in the server's zone and silently book a different instant than the person picked. Send `timeZoneName` (an IANA zone) alongside so the booking knows which zone it was made in.\n\n## Reservation types\n\nWhich `type` a caller may create depends on the roles they hold. Roles are additive: an instructor who is also a technician may create both sets.\n\n| Role | May create |\n|---|---|\n| owner, admin, dispatcher | solo, dual, ground, guest, sim, rental, maintenance |\n| instructor | solo, dual, ground, guest, sim |\n| student | solo, dual, ground, sim |\n| renter | rental |\n| technician | maintenance |\n\n## Rate limits\n\nLimits are applied per signed-in account, not per IP, so a whole school behind one office address does not share one budget.\n\n| | Limit |\n|---|---|\n| Per account | 300 requests/minute, 5,000/hour |\n| Unauthenticated | 100 requests per 5 minutes, per IP |\n| Sign-in and password reset | Tighter still, per IP and per email address |\n\nEvery response carries `RateLimit-Limit`, `RateLimit-Remaining`, and `RateLimit-Reset`. A `429` carries `Retry-After` in seconds — honour it rather than retrying on a fixed interval.\n\n## Pagination\n\nThere isn't any yet, and you should know that before you build against a list endpoint. Every collection returns its full set. Date-ranged endpoints (`/reservations`, the reports) are the ones to lean on for large data — `/reservations` requires a window for exactly this reason. Cursor pagination is planned, and will be additive: an unpaginated request will keep returning what it returns today.\n\n## Versioning\n\nThis document describes **v1.0.0**. Additive changes — new endpoints, new fields on an existing response — ship without notice, so parse leniently and ignore fields you do not recognise. Anything that would break a working integration gets a new major version and an announcement first.",
    "contact": {
      "name": "AerScheduler support",
      "email": "support@aerscheduler.com",
      "url": "https://aerscheduler.com/contact"
    },
    "license": {
      "name": "Proprietary",
      "url": "https://aerscheduler.com/terms-and-conditions"
    },
    "termsOfService": "https://aerscheduler.com/terms-and-conditions"
  },
  "servers": [
    {
      "url": "https://api.aerscheduler.com",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Authentication",
      "description": "Signing in, tokens, and account creation."
    },
    {
      "name": "Organizations",
      "description": "The flight school itself: membership, invitations, and settings."
    },
    {
      "name": "Members",
      "description": "People and the roles they hold."
    },
    {
      "name": "Reservations",
      "description": "Booking, rescheduling, and closing out flights."
    },
    {
      "name": "Availability",
      "description": "When people and aircraft are free."
    },
    {
      "name": "Resources",
      "description": "Aircraft, simulators, and rooms."
    },
    {
      "name": "Maintenance",
      "description": "Squawks and maintenance reminders."
    },
    {
      "name": "Invoices",
      "description": "Billing for completed flights."
    },
    {
      "name": "Locations",
      "description": "Bases of operation."
    },
    {
      "name": "Groups",
      "description": "Sets of members or resources."
    },
    {
      "name": "Currencies",
      "description": "Recurring requirements members must stay current on."
    },
    {
      "name": "Documents",
      "description": "Files held against members."
    },
    {
      "name": "Announcements",
      "description": "Notices to members."
    },
    {
      "name": "Notifications",
      "description": "Per-member in-app notifications."
    },
    {
      "name": "Reports",
      "description": "The reporting engine and saved views."
    },
    {
      "name": "Search",
      "description": "One search across the organization."
    },
    {
      "name": "Weather",
      "description": "Field observations."
    },
    {
      "name": "Service",
      "description": "Health and status."
    }
  ],
  "paths": {
    "/auth": {
      "post": {
        "tags": [
          "Authentication"
        ],
        "summary": "Sign in",
        "description": "Exchanges an email and password for a bearer token valid for **30 days**. Send that token as `Authorization: Bearer <token>` on every other request.\n\nThe token is scoped to one organization — whichever is active for the account. If the account belongs to several, switch with `POST /organizations/switch/{orgId}`, which returns a fresh token scoped to the new one.",
        "operationId": "signIn",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Credentials.",
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "password": {
                    "type": "string",
                    "format": "password"
                  }
                },
                "required": [
                  "email",
                  "password"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Signed in.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Invalid email or password. Deliberately identical whether or not the account exists.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many sign-in attempts. Limited per IP and per email address.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Authentication"
        ],
        "summary": "Get the current session",
        "description": "Returns the signed-in user, their active organization, and a refreshed token.",
        "responses": {
          "200": {
            "description": "The current session.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getSession"
      },
      "delete": {
        "tags": [
          "Authentication"
        ],
        "summary": "Sign out a device",
        "description": "Removes a push-notification device token. Bearer tokens are stateless and are not revoked by this call.",
        "responses": {
          "204": {
            "description": "Success. No body."
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "signOut",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "The device token to forget.",
                "properties": {
                  "token": {
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/auth/session": {
      "get": {
        "tags": [
          "Authentication"
        ],
        "summary": "Check whether a token is still valid",
        "description": "A cheap liveness probe for a stored token. Answers 200 if the token is good and 401 if it is not — nothing else. Use it to decide whether to prompt for sign-in without making a real request.",
        "operationId": "checkSession",
        "responses": {
          "200": {
            "description": "The token is valid."
          },
          "401": {
            "description": "The token is expired or invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/auth/forgot-password": {
      "post": {
        "tags": [
          "Authentication"
        ],
        "summary": "Request a password reset",
        "description": "Emails a reset link. Always answers 204, whether or not the address exists — telling the caller which addresses are registered would be an account-enumeration hole.",
        "operationId": "forgotPassword",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "The address to send to.",
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  }
                },
                "required": [
                  "email"
                ]
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Accepted."
          },
          "400": {
            "description": "No email supplied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/auth/reset-password": {
      "post": {
        "tags": [
          "Authentication"
        ],
        "summary": "Redeem a password reset token",
        "description": "Sets a new password using the token from the reset email.",
        "operationId": "resetPassword",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "The token and the new password.",
                "properties": {
                  "token": {
                    "type": "string"
                  },
                  "password": {
                    "type": "string",
                    "format": "password"
                  }
                },
                "required": [
                  "token",
                  "password"
                ]
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Password changed."
          },
          "400": {
            "description": "The link has expired or the token is invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/users": {
      "post": {
        "tags": [
          "Authentication"
        ],
        "summary": "Create an account",
        "description": "Registers a person and returns a token immediately. A verification email is sent; some endpoints stay closed until the address is verified.",
        "operationId": "signUp",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "The new account.",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "password": {
                    "type": "string",
                    "format": "password"
                  }
                },
                "required": [
                  "name",
                  "email",
                  "password"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Account created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "The address is already registered, or the password is too weak.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many sign-ups from this address.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Members"
        ],
        "summary": "List users",
        "description": "Every person in your organization, as `User` records. Prefer `GET /orgUsers`, which carries roles.",
        "responses": {
          "200": {
            "description": "The users.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/User"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "listUsers"
      },
      "patch": {
        "tags": [
          "Members"
        ],
        "summary": "Update your own profile",
        "description": "Changes your own name, phone, or other profile fields.",
        "responses": {
          "200": {
            "description": "Updated."
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "updateOwnProfile",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Fields to change.",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "phone": {
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Members"
        ],
        "summary": "Delete your own account",
        "description": "Soft-deletes the signed-in account.",
        "responses": {
          "204": {
            "description": "Success. No body."
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "deleteOwnAccount"
      }
    },
    "/users/{id}": {
      "get": {
        "tags": [
          "Members"
        ],
        "summary": "Get a user",
        "description": "One person in your organization.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/User"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such user in your organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getUser",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The user id."
          }
        ]
      }
    },
    "/users/{userId}/roles": {
      "get": {
        "tags": [
          "Members"
        ],
        "summary": "Get a member's roles",
        "description": "The roles a person holds in your organization. Requires the **admin** role.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/User"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getUserRoles",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The user id."
          }
        ]
      },
      "patch": {
        "tags": [
          "Members"
        ],
        "summary": "Set a member's roles",
        "description": "Replaces the whole role set — send every role you want them to keep, not just the change. Requires the **admin** role.\n\nTwo invariants are enforced: an organization must always have at least one owner and at least one administrator, and an owner must also be an administrator.",
        "responses": {
          "200": {
            "description": "Roles updated."
          },
          "400": {
            "description": "The change would leave the organization without an owner or an administrator.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "setUserRoles",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The user id."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "The complete role set.",
                "properties": {
                  "owner": {
                    "type": "boolean"
                  },
                  "admin": {
                    "type": "boolean"
                  },
                  "dispatcher": {
                    "type": "boolean"
                  },
                  "instructor": {
                    "type": "boolean"
                  },
                  "student": {
                    "type": "boolean"
                  },
                  "renter": {
                    "type": "boolean"
                  },
                  "technician": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/users/{userId}/approvedResources": {
      "get": {
        "tags": [
          "Members"
        ],
        "summary": "List a member's approved resources",
        "description": "Which aircraft this person has been checked out on.",
        "responses": {
          "200": {
            "description": "The approved resources.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Resource"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "listApprovedResources",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The user id."
          }
        ]
      }
    },
    "/users/availability": {
      "get": {
        "tags": [
          "Availability"
        ],
        "summary": "Get your recurring availability",
        "description": "Your standing weekly availability pattern.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Availability"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getOwnAvailability"
      },
      "patch": {
        "tags": [
          "Availability"
        ],
        "summary": "Set your recurring availability",
        "description": "Replaces your standing weekly availability.",
        "responses": {
          "200": {
            "description": "Updated."
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "setOwnAvailability",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "The new pattern.",
                "properties": {
                  "availability": {
                    "type": "array",
                    "items": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/organizations": {
      "get": {
        "tags": [
          "Organizations"
        ],
        "summary": "List your organizations",
        "description": "Every organization the signed-in account belongs to.",
        "responses": {
          "200": {
            "description": "The organizations.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Organization"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "listOrganizations"
      },
      "post": {
        "tags": [
          "Organizations"
        ],
        "summary": "Create an organization",
        "description": "Creates an organization, makes you its owner, and returns a token scoped to it. Its first location is created at the same time.",
        "responses": {
          "201": {
            "description": "Created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "Validation failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "createOrganization",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "The new organization.",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "organizationType": {
                    "type": "string",
                    "example": "flight school"
                  },
                  "about": {
                    "type": "string"
                  },
                  "showInDirectory": {
                    "type": "boolean"
                  },
                  "details": {
                    "type": "object",
                    "properties": {
                      "email": {
                        "type": "string"
                      },
                      "phone": {
                        "type": "string"
                      },
                      "address": {
                        "$ref": "#/components/schemas/Address"
                      }
                    }
                  },
                  "location": {
                    "type": "object",
                    "properties": {
                      "name": {
                        "type": "string"
                      },
                      "address": {
                        "$ref": "#/components/schemas/Address"
                      }
                    }
                  }
                },
                "required": [
                  "name",
                  "details",
                  "location"
                ]
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Organizations"
        ],
        "summary": "Update your organization",
        "description": "Requires the **admin** role.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Organization"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "updateOrganization",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Fields to change.",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "about": {
                    "type": "string"
                  },
                  "showInDirectory": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Organizations"
        ],
        "summary": "Delete your organization",
        "description": "Irreversible, and takes every record in it. Requires the **owner** role.",
        "responses": {
          "204": {
            "description": "Success. No body."
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "deleteOrganization"
      }
    },
    "/organizations/{orgId}": {
      "get": {
        "tags": [
          "Organizations"
        ],
        "summary": "Get an organization",
        "description": "Public detail for one organization.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Organization"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getOrganization",
        "parameters": [
          {
            "name": "orgId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The organization id."
          }
        ]
      }
    },
    "/organizations/switch/{orgId}": {
      "post": {
        "tags": [
          "Organizations"
        ],
        "summary": "Switch active organization",
        "description": "Returns a NEW token scoped to the given organization. Because a token carries exactly one organization, this is how a multi-organization integration moves between them — the old token keeps working and stays pointed at the old one.",
        "responses": {
          "200": {
            "description": "Switched.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "switchOrganization",
        "parameters": [
          {
            "name": "orgId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The organization to switch to."
          }
        ]
      }
    },
    "/organizations/join/{orgCode}": {
      "post": {
        "tags": [
          "Organizations"
        ],
        "summary": "Join an organization by code",
        "description": "Joins using the six-character code. If the organization is private this raises a join request for an administrator to approve instead, and answers 201.",
        "responses": {
          "200": {
            "description": "Joined.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthEnvelope"
                }
              }
            }
          },
          "201": {
            "description": "A request to join was raised and is awaiting approval.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "You already belong to this organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "joinOrganization",
        "parameters": [
          {
            "name": "orgCode",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The organization's join code."
          }
        ]
      }
    },
    "/organizations/invite": {
      "post": {
        "tags": [
          "Organizations"
        ],
        "summary": "Invite someone",
        "description": "Emails an invitation with the roles they will get on acceptance. Requires the **admin** role.",
        "responses": {
          "204": {
            "description": "Success. No body."
          },
          "400": {
            "description": "Already invited, or already a member.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "inviteMember",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Who to invite and as what.",
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "admin": {
                    "type": "boolean"
                  },
                  "instructor": {
                    "type": "boolean"
                  },
                  "student": {
                    "type": "boolean"
                  },
                  "renter": {
                    "type": "boolean"
                  },
                  "technician": {
                    "type": "boolean"
                  },
                  "dispatcher": {
                    "type": "boolean"
                  },
                  "orgUserGroupIds": {
                    "type": "array",
                    "items": {
                      "type": "integer"
                    }
                  }
                },
                "required": [
                  "email"
                ]
              }
            }
          }
        }
      }
    },
    "/organizations/removeUser/{userId}": {
      "post": {
        "tags": [
          "Organizations"
        ],
        "summary": "Remove a member",
        "description": "Removes someone from your organization. Requires the **admin** role.",
        "responses": {
          "204": {
            "description": "Success. No body."
          },
          "400": {
            "description": "They cannot be removed — the last owner or administrator, for instance.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "removeMember",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The user to remove."
          }
        ]
      }
    },
    "/organizations/leave/{orgId}": {
      "post": {
        "tags": [
          "Organizations"
        ],
        "summary": "Leave an organization",
        "description": "Removes yourself. The last owner cannot leave.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "204": {
            "description": "Success. No body."
          },
          "400": {
            "description": "You cannot leave — you are the last owner or administrator.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "leaveOrganization",
        "parameters": [
          {
            "name": "orgId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The organization to leave."
          }
        ]
      }
    },
    "/organizations/guests": {
      "get": {
        "tags": [
          "Organizations"
        ],
        "summary": "List guests",
        "description": "Non-members who have appeared on bookings.",
        "responses": {
          "200": {
            "description": "The guests.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/User"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "listGuests"
      }
    },
    "/organizations/ratings": {
      "get": {
        "tags": [
          "Organizations"
        ],
        "summary": "List ratings",
        "description": "The certificates and ratings your organization teaches.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "listRatings"
      },
      "post": {
        "tags": [
          "Organizations"
        ],
        "summary": "Create a rating",
        "description": "Requires the **admin** role.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "createRating",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "The rating.",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "anyInstructorCanTeach": {
                    "type": "boolean"
                  },
                  "defaultInstructorRate": {
                    "type": "integer",
                    "description": "Hourly rate in cents."
                  }
                },
                "required": [
                  "name",
                  "anyInstructorCanTeach",
                  "defaultInstructorRate"
                ]
              }
            }
          }
        }
      }
    },
    "/organizations/ratings/{ratingId}": {
      "patch": {
        "tags": [
          "Organizations"
        ],
        "summary": "Update a rating",
        "description": "Requires the **admin** role.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "updateRating",
        "parameters": [
          {
            "name": "ratingId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The rating id."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Fields to change.",
                "properties": {
                  "name": {
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Organizations"
        ],
        "summary": "Delete a rating",
        "description": "Requires the **admin** role.",
        "responses": {
          "204": {
            "description": "Success. No body."
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "deleteRating",
        "parameters": [
          {
            "name": "ratingId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The rating id."
          }
        ]
      }
    },
    "/invitations": {
      "get": {
        "tags": [
          "Organizations"
        ],
        "summary": "List pending invitations",
        "description": "Requires the **admin** role.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "listInvitations"
      }
    },
    "/joinRequests": {
      "get": {
        "tags": [
          "Organizations"
        ],
        "summary": "List join requests",
        "description": "People asking to join a private organization. Requires the **admin** role.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "listJoinRequests"
      }
    },
    "/joinRequests/{id}/accept": {
      "post": {
        "tags": [
          "Organizations"
        ],
        "summary": "Accept a join request",
        "description": "Requires the **admin** role.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "204": {
            "description": "Success. No body."
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "acceptJoinRequest",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The join request id."
          }
        ]
      }
    },
    "/joinRequests/{id}/decline": {
      "post": {
        "tags": [
          "Organizations"
        ],
        "summary": "Decline a join request",
        "description": "Requires the **admin** role.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "204": {
            "description": "Success. No body."
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "declineJoinRequest",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The join request id."
          }
        ]
      }
    },
    "/joinRequests/{id}": {
      "delete": {
        "tags": [
          "Organizations"
        ],
        "summary": "Delete a join request",
        "description": "Requires the **admin** role.",
        "responses": {
          "204": {
            "description": "Success. No body."
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "deleteJoinRequest",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The join request id."
          }
        ]
      }
    },
    "/orgUsers": {
      "get": {
        "tags": [
          "Members"
        ],
        "summary": "List members",
        "description": "Every member of your organization with the roles they hold. **This is the endpoint to use for “who is in this organization”** — the per-role routes (`/instructors`, `/students`, and so on) are unimplemented stubs and are not part of this API.",
        "responses": {
          "200": {
            "description": "The members.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/OrganizationUser"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "listOrgUsers"
      }
    },
    "/orgUsers/{orgUserId}": {
      "get": {
        "tags": [
          "Members"
        ],
        "summary": "Get a member",
        "description": "One membership record, with roles.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/OrganizationUser"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such member in your organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getOrgUser",
        "parameters": [
          {
            "name": "orgUserId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The membership id. Note this is NOT the user id."
          }
        ]
      }
    },
    "/orgUsers/preferences": {
      "get": {
        "tags": [
          "Members"
        ],
        "summary": "Get your notification preferences",
        "description": "What you have opted into, for this organization.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getPreferences"
      },
      "patch": {
        "tags": [
          "Members"
        ],
        "summary": "Update your notification preferences",
        "description": "Changes only the keys you send.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "updatePreferences",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Preferences to change.",
                "properties": {}
              }
            }
          }
        }
      }
    },
    "/reservations": {
      "get": {
        "tags": [
          "Reservations"
        ],
        "summary": "List reservations",
        "description": "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.",
        "responses": {
          "200": {
            "description": "The bookings.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Reservation"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The date range is missing or unparseable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "listReservations",
        "parameters": [
          {
            "name": "startDate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Start of the window, ISO 8601. Include an offset or `Z`."
          },
          {
            "name": "endDate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "End of the window, ISO 8601."
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Free-text filter. Matches the fields a person would search on."
          },
          {
            "name": "resourceId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "example": "12,15"
            },
            "description": "Comma-separated resource ids to filter by."
          },
          {
            "name": "locationId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated location ids to filter by."
          },
          {
            "name": "includeCanceled",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Set `true` to include cancelled bookings. Defaults to false."
          },
          {
            "name": "ongoing",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Set `false` to exclude bookings already in progress. Defaults to true."
          },
          {
            "name": "orderBy",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            },
            "description": "Sort direction on start time. Defaults to `desc`."
          }
        ]
      },
      "post": {
        "tags": [
          "Reservations"
        ],
        "summary": "Create a reservation",
        "description": "Books a resource, a person, or both.\n\n`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.\n\nWhich `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.\n\nPass a `recurrence` object to create a repeating series. Occurrences are real reservations, so each one can be ramped, reviewed, and invoiced independently.",
        "responses": {
          "201": {
            "description": "The booking that was created, or the series.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Reservation"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Rejected: a clash, a grounded aircraft, an ambiguous timestamp, or a type your roles may not create.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "createReservation",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "The booking.",
                "properties": {
                  "title": {
                    "type": "string",
                    "maxLength": 50,
                    "description": "Short label shown on the calendar.",
                    "example": "Discovery flight"
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "solo",
                      "dual",
                      "ground",
                      "guest",
                      "sim",
                      "rental",
                      "maintenance"
                    ],
                    "description": "What kind of booking. Restricted by the caller's roles — see the table above."
                  },
                  "start": {
                    "type": "string",
                    "format": "date-time",
                    "example": "2026-08-04T15:00:00-06:00",
                    "description": "Start instant. Must carry an explicit offset or `Z`."
                  },
                  "end": {
                    "type": "string",
                    "format": "date-time",
                    "example": "2026-08-04T17:00:00-06:00",
                    "description": "End instant. Must carry an explicit offset or `Z`."
                  },
                  "timeZoneName": {
                    "type": "string",
                    "example": "America/Denver",
                    "description": "IANA zone the booking is being made in — the field's zone, not the caller's device."
                  },
                  "notes": {
                    "type": "string",
                    "maxLength": 200,
                    "description": "Free-text notes shown on the booking."
                  },
                  "resource": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "integer"
                      }
                    },
                    "description": "The aircraft, simulator, or room to book, as `{ id }`."
                  },
                  "location": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "integer"
                      }
                    },
                    "description": "Where it happens, as `{ id }`. Defaults to the resource's location."
                  },
                  "personnel": {
                    "type": "object",
                    "description": "Who is on the booking. One person cannot fill two seats on the same booking.",
                    "properties": {
                      "instructors": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "id": {
                              "type": "integer"
                            }
                          }
                        }
                      },
                      "students": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "id": {
                              "type": "integer"
                            }
                          }
                        }
                      },
                      "renters": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "id": {
                              "type": "integer"
                            }
                          }
                        }
                      },
                      "guests": {
                        "type": "array",
                        "items": {
                          "type": "object"
                        }
                      }
                    }
                  },
                  "recurrence": {
                    "type": "object",
                    "description": "Optional. Creates a repeating series."
                  }
                },
                "required": [
                  "title",
                  "type",
                  "start",
                  "end",
                  "timeZoneName"
                ]
              }
            }
          }
        }
      }
    },
    "/reservations/{id}": {
      "get": {
        "tags": [
          "Reservations"
        ],
        "summary": "Get a reservation",
        "description": "One booking, with personnel and close-out figures.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Reservation"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such booking in your organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getReservation",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The reservation id."
          }
        ]
      },
      "patch": {
        "tags": [
          "Reservations"
        ],
        "summary": "Update a reservation",
        "description": "Reschedules or edits a booking. The same clash and role rules as create apply.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Reservation"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Rejected — usually a clash.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "updateReservation",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The reservation id."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Fields to change.",
                "properties": {
                  "title": {
                    "type": "string"
                  },
                  "start": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "end": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "notes": {
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Reservations"
        ],
        "summary": "Cancel a reservation",
        "description": "Cancels rather than destroys: the booking stays readable with `cancelledAt` set, which is what keeps cancellation reporting honest. Send a `cancellationCategory` so it lands in the right bucket.",
        "responses": {
          "204": {
            "description": "Success. No body."
          },
          "400": {
            "description": "It is already cancelled, or too late to cancel under your organization's policy.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "cancelReservation",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The reservation id."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Why it was cancelled.",
                "properties": {
                  "cancellationReason": {
                    "type": "string",
                    "maxLength": 60
                  },
                  "cancellationCategory": {
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/reservations/resource/{resourceId}": {
      "get": {
        "tags": [
          "Reservations"
        ],
        "summary": "List a resource's reservations",
        "description": "Bookings for one aircraft, simulator, or room in a window.",
        "responses": {
          "200": {
            "description": "The bookings.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Reservation"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "listReservationsForResource",
        "parameters": [
          {
            "name": "resourceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The resource id."
          },
          {
            "name": "startDate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Start of the window, ISO 8601. Include an offset or `Z`."
          },
          {
            "name": "endDate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "End of the window, ISO 8601."
          }
        ]
      }
    },
    "/reservations/user/{userId}": {
      "get": {
        "tags": [
          "Reservations"
        ],
        "summary": "List a person's reservations",
        "description": "Bookings a person is on, in a window.",
        "responses": {
          "200": {
            "description": "The bookings.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Reservation"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "listReservationsForUser",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The user id."
          },
          {
            "name": "startDate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Start of the window, ISO 8601. Include an offset or `Z`."
          },
          {
            "name": "endDate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "End of the window, ISO 8601."
          }
        ]
      }
    },
    "/reservations/{id}/rampOut": {
      "post": {
        "tags": [
          "Reservations"
        ],
        "summary": "Ramp out",
        "description": "Records departure meter readings and marks the aircraft off the ramp. The first step of closing a flight out.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Reservation"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The aircraft is already ramped out, or you are not on this booking.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "rampOut",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The reservation id."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Departure readings.",
                "properties": {
                  "hobbsTimeOut": {
                    "type": "number"
                  },
                  "tachTimeOut": {
                    "type": "number"
                  }
                },
                "required": [
                  "hobbsTimeOut",
                  "tachTimeOut"
                ]
              }
            }
          }
        }
      }
    },
    "/reservations/{id}/rampIn": {
      "post": {
        "tags": [
          "Reservations"
        ],
        "summary": "Ramp in",
        "description": "Records return meter readings and puts the aircraft back on the ramp.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Reservation"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Not ramped out, or you are not on this booking.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "rampIn",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The reservation id."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Return readings.",
                "properties": {
                  "hobbsTimeIn": {
                    "type": "number"
                  },
                  "tachTimeIn": {
                    "type": "number"
                  }
                },
                "required": [
                  "hobbsTimeIn",
                  "tachTimeIn"
                ]
              }
            }
          }
        }
      }
    },
    "/reservations/{id}/confirmReview": {
      "post": {
        "tags": [
          "Reservations"
        ],
        "summary": "Sign off the close-out",
        "description": "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.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Reservation"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Nothing to confirm yet.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "confirmReview",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The reservation id."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "No fields required.",
                "properties": {}
              }
            }
          }
        }
      }
    },
    "/reservations/{id}/invoices": {
      "post": {
        "tags": [
          "Reservations"
        ],
        "summary": "Invoice a reservation",
        "description": "Generates the bill for a closed-out flight. Requires the **admin** role.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Invoice"
                    }
                  }
                }
              }
            }
          },
          "201": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Invoice"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The flight is not closed out, or it is already invoiced.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "createInvoiceForReservation",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The reservation id."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "No fields required.",
                "properties": {}
              }
            }
          }
        }
      }
    },
    "/availability/me": {
      "get": {
        "tags": [
          "Availability"
        ],
        "summary": "Your free windows",
        "description": "When you are free in a window, after bookings are subtracted.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Availability"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getMyAvailability",
        "parameters": [
          {
            "name": "startDate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Start of the window, ISO 8601. Include an offset or `Z`."
          },
          {
            "name": "endDate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "End of the window, ISO 8601."
          }
        ]
      }
    },
    "/availability/user/{userId}": {
      "get": {
        "tags": [
          "Availability"
        ],
        "summary": "A person's free windows",
        "description": "Used to find an instructor's open slots.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Availability"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getUserAvailability",
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The user id."
          },
          {
            "name": "startDate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Start of the window, ISO 8601. Include an offset or `Z`."
          },
          {
            "name": "endDate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "End of the window, ISO 8601."
          }
        ]
      }
    },
    "/availability/resource/{id}": {
      "get": {
        "tags": [
          "Availability"
        ],
        "summary": "A resource's free windows",
        "description": "Used to find an aircraft's open slots before booking.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Availability"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getResourceAvailability",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The resource id."
          },
          {
            "name": "startDate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Start of the window, ISO 8601. Include an offset or `Z`."
          },
          {
            "name": "endDate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "End of the window, ISO 8601."
          }
        ]
      }
    },
    "/resources/planes": {
      "get": {
        "tags": [
          "Resources"
        ],
        "summary": "List aircraft",
        "description": "Only resources whose type is a plane.",
        "responses": {
          "200": {
            "description": "The aircraft.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Resource"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "listPlanes",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Free-text filter. Matches the fields a person would search on."
          },
          {
            "name": "grounded",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Filter on grounded state."
          },
          {
            "name": "locationId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated location ids."
          }
        ]
      }
    },
    "/resources/simulators": {
      "get": {
        "tags": [
          "Resources"
        ],
        "summary": "List simulators",
        "description": "Only resources whose type is a simulator.",
        "responses": {
          "200": {
            "description": "The simulators.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Resource"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "listSimulators",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Free-text filter. Matches the fields a person would search on."
          },
          {
            "name": "grounded",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Filter on grounded state."
          },
          {
            "name": "locationId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated location ids."
          }
        ]
      }
    },
    "/resources/rooms": {
      "get": {
        "tags": [
          "Resources"
        ],
        "summary": "List rooms",
        "description": "Only resources whose type is a room.",
        "responses": {
          "200": {
            "description": "The rooms.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Resource"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "listRooms",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Free-text filter. Matches the fields a person would search on."
          },
          {
            "name": "locationId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated location ids."
          }
        ]
      }
    },
    "/resources/{id}/fuel": {
      "post": {
        "tags": [
          "Resources"
        ],
        "summary": "Record fuel on board",
        "description": "Updates fuel for one or both engines.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "updateFuel",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The resource id."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Fuel figures, in the aircraft's configured units.",
                "properties": {
                  "fuelEngine1": {
                    "type": "integer"
                  },
                  "fuelEngine2": {
                    "type": "integer"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/resources/{id}/approve": {
      "post": {
        "tags": [
          "Resources"
        ],
        "summary": "Approve a member for a resource",
        "description": "Checks someone out on an aircraft. Requires the **admin** role.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "That person is not in your organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "approveResource",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The resource id."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Who to approve.",
                "properties": {
                  "userId": {
                    "type": "integer"
                  }
                },
                "required": [
                  "userId"
                ]
              }
            }
          }
        }
      }
    },
    "/resources/{id}/unapprove": {
      "post": {
        "tags": [
          "Resources"
        ],
        "summary": "Withdraw a member's approval",
        "description": "Requires the **admin** role.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "That person is not in your organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "unapproveResource",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The resource id."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Whose approval to withdraw.",
                "properties": {
                  "userId": {
                    "type": "integer"
                  }
                },
                "required": [
                  "userId"
                ]
              }
            }
          }
        }
      }
    },
    "/maintenance/squawks": {
      "get": {
        "tags": [
          "Maintenance"
        ],
        "summary": "List squawks",
        "description": "Reported discrepancies. Visible to technicians, dispatchers, and administrators.",
        "responses": {
          "200": {
            "description": "The squawks.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Squawk"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "listSquawks",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Free-text filter. Matches the fields a person would search on."
          },
          {
            "name": "resolved",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Filter on resolved state."
          },
          {
            "name": "resourceId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated resource ids."
          },
          {
            "name": "startDate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Start of the window, ISO 8601. Include an offset or `Z`."
          },
          {
            "name": "endDate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "End of the window, ISO 8601."
          }
        ]
      },
      "post": {
        "tags": [
          "Maintenance"
        ],
        "summary": "Report a squawk",
        "description": "Note the resource is given as a flat `resourceId` here, where reservations take a nested `resource: { id }`. That inconsistency is real and is on the list to reconcile.",
        "responses": {
          "201": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Squawk"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "createSquawk",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "The discrepancy.",
                "properties": {
                  "title": {
                    "type": "string"
                  },
                  "description": {
                    "type": "string"
                  },
                  "resourceId": {
                    "type": "integer"
                  }
                },
                "required": [
                  "title",
                  "description",
                  "resourceId"
                ]
              }
            }
          }
        }
      }
    },
    "/maintenance/squawks/{squawkId}": {
      "get": {
        "tags": [
          "Maintenance"
        ],
        "summary": "Get a squawk",
        "description": "One reported discrepancy.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Squawk"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such squawk.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getSquawk",
        "parameters": [
          {
            "name": "squawkId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The squawk id."
          }
        ]
      },
      "post": {
        "tags": [
          "Maintenance"
        ],
        "summary": "Act on a squawk",
        "description": "Resolves or reopens a squawk. This is a POST rather than a PATCH for historical reasons; an `action` field in the body is required.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Squawk"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such squawk, or no `action` was supplied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "updateSquawk",
        "parameters": [
          {
            "name": "squawkId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The squawk id."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "What to do.",
                "properties": {
                  "action": {
                    "type": "string"
                  },
                  "title": {
                    "type": "string"
                  },
                  "description": {
                    "type": "string"
                  }
                },
                "required": [
                  "action"
                ]
              }
            }
          }
        }
      }
    },
    "/maintenance/resources/{resourceId}/squawks": {
      "get": {
        "tags": [
          "Maintenance"
        ],
        "summary": "List an aircraft's squawks",
        "description": "Discrepancies against one resource.",
        "responses": {
          "200": {
            "description": "The squawks.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Squawk"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "listSquawksForResource",
        "parameters": [
          {
            "name": "resourceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The resource id."
          },
          {
            "name": "resolved",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Filter on resolved state."
          }
        ]
      }
    },
    "/maintenance/reminders": {
      "get": {
        "tags": [
          "Maintenance"
        ],
        "summary": "List maintenance reminders",
        "description": "Live reminders across the fleet.",
        "responses": {
          "200": {
            "description": "The reminders.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MaintenanceReminder"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "listReminders"
      }
    },
    "/maintenance/reminders/{maintenanceReminderId}": {
      "get": {
        "tags": [
          "Maintenance"
        ],
        "summary": "Get a maintenance reminder",
        "description": "One reminder.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/MaintenanceReminder"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such reminder.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getReminder",
        "parameters": [
          {
            "name": "maintenanceReminderId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The reminder id."
          }
        ]
      },
      "post": {
        "tags": [
          "Maintenance"
        ],
        "summary": "Sign off a maintenance reminder",
        "description": "Marks the work done, which starts the next interval if the template repeats. POST rather than PATCH for historical reasons.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/MaintenanceReminder"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "completeReminder",
        "parameters": [
          {
            "name": "maintenanceReminderId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The reminder id."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Completion detail.",
                "properties": {
                  "completedAt": {
                    "type": "string",
                    "format": "date-time"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/invoices": {
      "get": {
        "tags": [
          "Invoices"
        ],
        "summary": "List invoices",
        "description": "Every invoice in the organization. **Administrators only** — this is the whole membership's payment history plus revenue aggregates, so it is treated as a financial surface rather than an operational one. Dispatchers get the per-reservation invoice instead.",
        "responses": {
          "200": {
            "description": "The invoices.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Invoice"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The date range is unparseable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "listInvoices",
        "parameters": [
          {
            "name": "startDate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Start of the window, ISO 8601. Include an offset or `Z`."
          },
          {
            "name": "endDate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "End of the window, ISO 8601."
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Free-text filter. Matches the fields a person would search on."
          },
          {
            "name": "paid",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Filter on paid state."
          }
        ]
      }
    },
    "/invoices/{invoiceId}": {
      "get": {
        "tags": [
          "Invoices"
        ],
        "summary": "Get an invoice",
        "description": "One invoice with its line items.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Invoice"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such invoice.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getInvoice",
        "parameters": [
          {
            "name": "invoiceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The invoice id."
          }
        ]
      },
      "patch": {
        "tags": [
          "Invoices"
        ],
        "summary": "Update an invoice",
        "description": "Adjusts line items or marks it paid. Requires the **admin** role.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Invoice"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "updateInvoice",
        "parameters": [
          {
            "name": "invoiceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The invoice id."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Fields to change.",
                "properties": {}
              }
            }
          }
        }
      }
    },
    "/invoices/{invoiceId}/remind": {
      "post": {
        "tags": [
          "Invoices"
        ],
        "summary": "Send a payment reminder",
        "description": "Emails the member about an outstanding invoice. Requires the **admin** role.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "204": {
            "description": "Success. No body."
          },
          "400": {
            "description": "Already paid, or reminded too recently.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "remindInvoice",
        "parameters": [
          {
            "name": "invoiceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The invoice id."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "No fields required.",
                "properties": {}
              }
            }
          }
        }
      }
    },
    "/invoices/orgUsers/{orgUserId}": {
      "get": {
        "tags": [
          "Invoices"
        ],
        "summary": "List a member's invoices",
        "description": "Your own, or anyone's if you are an administrator.",
        "responses": {
          "200": {
            "description": "The invoices.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Invoice"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Not your invoices, and you are not an administrator.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "listInvoicesForMember",
        "parameters": [
          {
            "name": "orgUserId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The membership id."
          },
          {
            "name": "startDate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Start of the window, ISO 8601. Include an offset or `Z`."
          },
          {
            "name": "endDate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "End of the window, ISO 8601."
          },
          {
            "name": "paid",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Filter on paid state."
          }
        ]
      }
    },
    "/invoices/reservation/{reservationId}": {
      "get": {
        "tags": [
          "Invoices"
        ],
        "summary": "Get a reservation's invoice",
        "description": "The bill for one flight. Dispatchers can read this even though they cannot list the organization's invoices.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Invoice"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "That flight has not been invoiced.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getInvoiceForReservation",
        "parameters": [
          {
            "name": "reservationId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The reservation id."
          }
        ]
      }
    },
    "/invoices/reservation/{reservationId}/invoiceTotals": {
      "get": {
        "tags": [
          "Invoices"
        ],
        "summary": "Get a reservation's totals",
        "description": "What the flight comes to, broken down, without creating an invoice.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such reservation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getInvoiceTotalsForReservation",
        "parameters": [
          {
            "name": "reservationId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The reservation id."
          }
        ]
      }
    },
    "/announcements": {
      "get": {
        "tags": [
          "Announcements"
        ],
        "summary": "List announcements",
        "description": "Live notices for your organization. Expired ones are swept hourly.",
        "responses": {
          "200": {
            "description": "The announcements.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Announcement"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "listAnnouncements"
      },
      "post": {
        "tags": [
          "Announcements"
        ],
        "summary": "Post an announcement",
        "description": "Notifies the roles you target. Requires the **admin** role.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Announcement"
                    }
                  }
                }
              }
            }
          },
          "201": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Announcement"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "createAnnouncement",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "The notice.",
                "properties": {
                  "title": {
                    "type": "string"
                  },
                  "message": {
                    "type": "string"
                  },
                  "expireAt": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "forRoles": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                },
                "required": [
                  "title",
                  "message"
                ]
              }
            }
          }
        }
      }
    },
    "/announcements/{id}": {
      "patch": {
        "tags": [
          "Announcements"
        ],
        "summary": "Update an announcement",
        "description": "Requires the **admin** role.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Announcement"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "updateAnnouncement",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The announcement id."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Fields to change.",
                "properties": {
                  "title": {
                    "type": "string"
                  },
                  "message": {
                    "type": "string"
                  },
                  "expireAt": {
                    "type": "string",
                    "format": "date-time"
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Announcements"
        ],
        "summary": "Delete an announcement",
        "description": "Requires the **admin** role.",
        "responses": {
          "204": {
            "description": "Success. No body."
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "deleteAnnouncement",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The announcement id."
          }
        ]
      }
    },
    "/notifications": {
      "get": {
        "tags": [
          "Notifications"
        ],
        "summary": "List your notifications",
        "description": "Your own in-app notifications.",
        "responses": {
          "200": {
            "description": "The notifications.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Notification"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "listNotifications",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Free-text filter. Matches the fields a person would search on."
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "unread",
                "read",
                "all"
              ]
            },
            "description": "Filter by read state. Defaults to `all`."
          }
        ]
      },
      "post": {
        "tags": [
          "Notifications"
        ],
        "summary": "Clear all your notifications",
        "description": "Removes every notification for the signed-in member. A POST rather than a DELETE for historical reasons.",
        "responses": {
          "204": {
            "description": "Success. No body."
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "clearNotifications",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "No fields required.",
                "properties": {}
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Notifications"
        ],
        "summary": "Register a push device token",
        "description": "Despite the path, this does not update notifications — it registers a device for push. A separate concern that shares the route for historical reasons.",
        "responses": {
          "200": {
            "description": "Registered."
          },
          "400": {
            "description": "No token supplied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "registerDeviceToken",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "The device token.",
                "properties": {
                  "token": {
                    "type": "string"
                  }
                },
                "required": [
                  "token"
                ]
              }
            }
          }
        }
      }
    },
    "/notifications/{notificationId}": {
      "post": {
        "tags": [
          "Notifications"
        ],
        "summary": "Mark a notification read",
        "description": "A POST rather than a PATCH for historical reasons.",
        "responses": {
          "204": {
            "description": "Success. No body."
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "That notification is not yours.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "markNotificationRead",
        "parameters": [
          {
            "name": "notificationId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The notification id."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "No fields required.",
                "properties": {}
              }
            }
          }
        }
      }
    },
    "/currencies": {
      "get": {
        "tags": [
          "Currencies"
        ],
        "summary": "List currencies",
        "description": "Every member's standing against every tracked requirement.",
        "responses": {
          "200": {
            "description": "The currency records.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Currency"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "listCurrencies"
      }
    },
    "/currencies/{currencyId}": {
      "get": {
        "tags": [
          "Currencies"
        ],
        "summary": "Get a currency record",
        "description": "Note this takes a CURRENCY id, not a currency-type id — the two are one path segment apart and are easy to confuse.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Currency"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Not yours to see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such currency.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getCurrency",
        "parameters": [
          {
            "name": "currencyId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The currency id."
          }
        ]
      },
      "patch": {
        "tags": [
          "Currencies"
        ],
        "summary": "Update a currency record",
        "description": "Adjusts the dates on one member's standing.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Currency"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Not yours to edit.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "updateCurrency",
        "parameters": [
          {
            "name": "currencyId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The currency id."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Fields to change.",
                "properties": {}
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Currencies"
        ],
        "summary": "Renew a currency",
        "description": "Signs off a renewal and starts a new period. Who may do this is governed by the currency type's `canRenewSelf` / `instructorCanRenew` / `dispatcherCanRenew` flags.",
        "responses": {
          "201": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Currency"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "You may not renew this one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "renewCurrency",
        "parameters": [
          {
            "name": "currencyId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The currency id."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "The renewal.",
                "properties": {
                  "completedAt": {
                    "type": "string",
                    "format": "date-time"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/currencies/types/{currencyTypeId}/currencies": {
      "get": {
        "tags": [
          "Currencies"
        ],
        "summary": "List a type's currency records",
        "description": "Everyone's standing against one requirement.",
        "responses": {
          "200": {
            "description": "The currency records.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Currency"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "listCurrenciesForType",
        "parameters": [
          {
            "name": "currencyTypeId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The currency type id."
          }
        ]
      }
    },
    "/userDocuments": {
      "post": {
        "tags": [
          "Documents"
        ],
        "summary": "Upload a document",
        "description": "Records a document against a member. Administrators may upload on someone else's behalf; whether a member can upload their own depends on the document type's `restricted` flag.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/UserDocument"
                    }
                  }
                }
              }
            }
          },
          "201": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/UserDocument"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "createUserDocument",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "The document.",
                "properties": {
                  "documentTypeId": {
                    "type": "integer"
                  },
                  "orgUserId": {
                    "type": "integer"
                  },
                  "name": {
                    "type": "string"
                  },
                  "expiresAt": {
                    "type": "string",
                    "format": "date-time"
                  }
                },
                "required": [
                  "documentTypeId",
                  "orgUserId"
                ]
              }
            }
          }
        }
      }
    },
    "/userDocuments/{documentId}": {
      "get": {
        "tags": [
          "Documents"
        ],
        "summary": "Get a document",
        "description": "One document record.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/UserDocument"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such document.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getUserDocument",
        "parameters": [
          {
            "name": "documentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The document id."
          }
        ]
      },
      "patch": {
        "tags": [
          "Documents"
        ],
        "summary": "Update a document",
        "description": "Renames it or changes its expiry.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/UserDocument"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "updateUserDocument",
        "parameters": [
          {
            "name": "documentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The document id."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Fields to change.",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "expiresAt": {
                    "type": "string",
                    "format": "date-time"
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Documents"
        ],
        "summary": "Delete a document",
        "description": "Removes the document.",
        "responses": {
          "204": {
            "description": "Success. No body."
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "deleteUserDocument",
        "parameters": [
          {
            "name": "documentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The document id."
          }
        ]
      }
    },
    "/userDocuments/{documentId}/signedUrl": {
      "post": {
        "tags": [
          "Documents"
        ],
        "summary": "Get a signed URL for a document",
        "description": "Returns a short-lived URL to upload or download the file. Documents are not served from a public URL.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getUserDocumentSignedUrl",
        "parameters": [
          {
            "name": "documentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The document id."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "No fields required.",
                "properties": {}
              }
            }
          }
        }
      }
    },
    "/userDocuments/orgUsers/{orgUserId}": {
      "get": {
        "tags": [
          "Documents"
        ],
        "summary": "List a member's documents",
        "description": "Everything held against one member.",
        "responses": {
          "200": {
            "description": "The documents.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/UserDocument"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "listUserDocumentsForMember",
        "parameters": [
          {
            "name": "orgUserId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The membership id."
          }
        ]
      }
    },
    "/reports/catalog": {
      "get": {
        "tags": [
          "Reports"
        ],
        "summary": "List available reports",
        "description": "Every report you may run, with its id and category. **Start here**: the ids returned are what `POST /reports/run` and `POST /reports/export` take, and the catalog is filtered to what your roles allow, so you never see a report you cannot run.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "timeZone": {
                          "type": "string"
                        },
                        "categories": {
                          "type": "array",
                          "items": {
                            "type": "object"
                          }
                        },
                        "reports": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/ReportDefinition"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "listReports"
      }
    },
    "/reports/run": {
      "post": {
        "tags": [
          "Reports"
        ],
        "summary": "Run a report",
        "description": "Runs one report from the catalog and returns columns, rows, and totals.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ReportResult"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown report id, or an unparseable window.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "runReport",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Which report, over what window.",
                "properties": {
                  "reportId": {
                    "type": "string",
                    "example": "revenue"
                  },
                  "startDate": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "endDate": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "config": {
                    "type": "object"
                  }
                },
                "required": [
                  "reportId"
                ]
              }
            }
          }
        }
      }
    },
    "/reports/export": {
      "post": {
        "tags": [
          "Reports"
        ],
        "summary": "Export a report as CSV",
        "description": "Same inputs as `run`, but the response is `text/csv` rather than the usual JSON envelope.",
        "responses": {
          "200": {
            "description": "The report as CSV.",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Unknown report id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "exportReport",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Which report, over what window.",
                "properties": {
                  "reportId": {
                    "type": "string"
                  },
                  "startDate": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "endDate": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "format": {
                    "type": "string",
                    "enum": [
                      "csv"
                    ]
                  }
                },
                "required": [
                  "reportId"
                ]
              }
            }
          }
        }
      }
    },
    "/reports/overview": {
      "get": {
        "tags": [
          "Reports"
        ],
        "summary": "Organization overview",
        "description": "The headline numbers for a window — the figures behind the dashboard.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getReportOverview",
        "parameters": [
          {
            "name": "startDate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Start of the window, ISO 8601. Include an offset or `Z`."
          },
          {
            "name": "endDate",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "End of the window, ISO 8601."
          }
        ]
      }
    },
    "/search": {
      "get": {
        "tags": [
          "Search"
        ],
        "summary": "Search across the organization",
        "description": "One search over aircraft, members, bookings, and locations. The results are narrowed to what your roles allow, per entity type — so this is safe to expose to any member.\n\n`q` is optional: omitting it returns the newest few of each type, which is what a search screen shows before anyone types.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/SearchResults"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "search",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Free-text filter. Matches the fields a person would search on."
          },
          {
            "name": "types",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "example": "resource,orgUser"
            },
            "description": "Comma-separated entity types to search. Defaults to all."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Maximum matches per type."
          }
        ]
      }
    },
    "/weather/metar": {
      "get": {
        "tags": [
          "Weather"
        ],
        "summary": "Current METAR",
        "description": "The latest observation for a field.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown or missing airport identifier.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getMetar",
        "parameters": [
          {
            "name": "airport",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "example": "KAPA"
            },
            "description": "ICAO identifier."
          }
        ]
      }
    },
    "/health": {
      "get": {
        "tags": [
          "Service"
        ],
        "summary": "Health check",
        "description": "Answers 200 while the service is up. Not rate limited, and does not use the standard `{ data }` envelope.",
        "operationId": "health",
        "security": [],
        "responses": {
          "200": {
            "description": "The service is up.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "health": {
                      "type": "string",
                      "example": "server is running"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/locations": {
      "get": {
        "tags": [
          "Locations"
        ],
        "summary": "List locations",
        "description": "Every location in your organization.",
        "responses": {
          "200": {
            "description": "The locations.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Location"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "listLocations",
        "parameters": []
      },
      "post": {
        "tags": [
          "Locations"
        ],
        "summary": "Create a location",
        "description": "Adds a location to your organization. Requires the **admin** role.",
        "responses": {
          "201": {
            "description": "The location that was created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Location"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The body failed validation. `message` says which field.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "createLocation",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "The new location. The address is geocoded on the way in and is rejected if it cannot be resolved.",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "address": {
                    "$ref": "#/components/schemas/Address"
                  }
                },
                "required": [
                  "name",
                  "address"
                ]
              }
            }
          }
        }
      }
    },
    "/locations/{id}": {
      "get": {
        "tags": [
          "Locations"
        ],
        "summary": "Get a location",
        "description": "Fetches one location by id.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Location"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such location, or it belongs to another organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getLocation",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The location id."
          }
        ]
      },
      "patch": {
        "tags": [
          "Locations"
        ],
        "summary": "Update a location",
        "description": "Changes the fields you send and leaves the rest alone. Requires the **admin** role.",
        "responses": {
          "200": {
            "description": "The updated location.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Location"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The body failed validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "updateLocation",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The location id."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Fields to change.",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "timeZone": {
                    "type": "string"
                  },
                  "address": {
                    "$ref": "#/components/schemas/Address"
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Locations"
        ],
        "summary": "Delete a location",
        "description": "Removes the location. Requires the **admin** role.",
        "responses": {
          "204": {
            "description": "Success. No body."
          },
          "400": {
            "description": "The location could not be deleted — usually because something still references it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "deleteLocation",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The location id."
          }
        ]
      }
    },
    "/resources": {
      "get": {
        "tags": [
          "Resources"
        ],
        "summary": "List resources",
        "description": "Every bookable resource — aircraft, simulators, and rooms together. Use `/resources/planes`, `/simulators`, or `/rooms` for one kind.",
        "responses": {
          "200": {
            "description": "The resources.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Resource"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "listResources",
        "parameters": []
      },
      "post": {
        "tags": [
          "Resources"
        ],
        "summary": "Create a resource",
        "description": "Adds a resource to your organization. Requires the **admin** role.",
        "responses": {
          "201": {
            "description": "The resource that was created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Resource"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The body failed validation. `message` says which field.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "createResource",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "The new resource. Exactly one of `type.plane`, `type.simulator`, or `type.room` must be present, and `location.id` is required.",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "location": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "integer"
                      }
                    }
                  },
                  "type": {
                    "type": "object",
                    "properties": {
                      "plane": {
                        "$ref": "#/components/schemas/Plane"
                      },
                      "simulator": {
                        "type": "object"
                      },
                      "room": {
                        "type": "object"
                      }
                    }
                  }
                },
                "required": [
                  "name",
                  "location",
                  "type"
                ]
              }
            }
          }
        }
      }
    },
    "/resources/{id}": {
      "get": {
        "tags": [
          "Resources"
        ],
        "summary": "Get a resource",
        "description": "Fetches one resource by id.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Resource"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such resource, or it belongs to another organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getResource",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The resource id."
          }
        ]
      },
      "patch": {
        "tags": [
          "Resources"
        ],
        "summary": "Update a resource",
        "description": "Changes the fields you send and leaves the rest alone. Requires the **admin** role.",
        "responses": {
          "200": {
            "description": "The updated resource.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Resource"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The body failed validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "updateResource",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The resource id."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Fields to change. `type` must be present even when you are only changing a top-level field.",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "type": {
                    "type": "object"
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Resources"
        ],
        "summary": "Delete a resource",
        "description": "Removes the resource. Requires the **admin** role.",
        "responses": {
          "204": {
            "description": "Success. No body."
          },
          "400": {
            "description": "The resource could not be deleted — usually because something still references it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "deleteResource",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The resource id."
          }
        ]
      }
    },
    "/currencies/types": {
      "get": {
        "tags": [
          "Currencies"
        ],
        "summary": "List currency types",
        "description": "Every currency type in your organization.",
        "responses": {
          "200": {
            "description": "The currency types.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CurrencyType"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "listCurrencyTypes",
        "parameters": []
      },
      "post": {
        "tags": [
          "Currencies"
        ],
        "summary": "Create a currency type",
        "description": "Adds a currency type to your organization. Requires the **admin** role.",
        "responses": {
          "201": {
            "description": "The currency type that was created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CurrencyType"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The body failed validation. `message` says which field.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "createCurrencyType",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "The requirement to track. Attaching member groups is what actually creates the per-member `Currency` records — a type created with no groups tracks nobody.",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "description": {
                    "type": "string"
                  },
                  "expiresInMonths": {
                    "type": "integer"
                  },
                  "expiresInDays": {
                    "type": "integer"
                  },
                  "warningPeriodInDays": {
                    "type": "integer"
                  },
                  "canRenewSelf": {
                    "type": "boolean"
                  },
                  "instructorCanRenew": {
                    "type": "boolean"
                  },
                  "dispatcherCanRenew": {
                    "type": "boolean"
                  },
                  "orgUserGroupIds": {
                    "type": "array",
                    "items": {
                      "type": "integer"
                    }
                  }
                },
                "required": [
                  "name"
                ]
              }
            }
          }
        }
      }
    },
    "/currencies/types/{currencyTypeId}": {
      "get": {
        "tags": [
          "Currencies"
        ],
        "summary": "Get a currency type",
        "description": "Fetches one currency type by id.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CurrencyType"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such currency type, or it belongs to another organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getCurrencyType",
        "parameters": [
          {
            "name": "currencyTypeId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The currency type id."
          }
        ]
      },
      "patch": {
        "tags": [
          "Currencies"
        ],
        "summary": "Update a currency type",
        "description": "Changes the fields you send and leaves the rest alone. Requires the **admin** role.",
        "responses": {
          "200": {
            "description": "The updated currency type.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CurrencyType"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The body failed validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "updateCurrencyType",
        "parameters": [
          {
            "name": "currencyTypeId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The currency type id."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Fields to change.",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "active": {
                    "type": "boolean"
                  },
                  "expiresInMonths": {
                    "type": "integer"
                  },
                  "orgUserGroupIds": {
                    "type": "array",
                    "items": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Currencies"
        ],
        "summary": "Delete a currency type",
        "description": "Removes the currency type. Requires the **admin** role.",
        "responses": {
          "204": {
            "description": "Success. No body."
          },
          "400": {
            "description": "The currency type could not be deleted — usually because something still references it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "deleteCurrencyType",
        "parameters": [
          {
            "name": "currencyTypeId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The currency type id."
          }
        ]
      }
    },
    "/userDocuments/types": {
      "get": {
        "tags": [
          "Documents"
        ],
        "summary": "List document types",
        "description": "Every document type in your organization.",
        "responses": {
          "200": {
            "description": "The document types.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DocumentType"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "listDocumentTypes",
        "parameters": []
      },
      "post": {
        "tags": [
          "Documents"
        ],
        "summary": "Create a document type",
        "description": "Adds a document type to your organization. Requires the **admin** role.",
        "responses": {
          "201": {
            "description": "The document type that was created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/DocumentType"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The body failed validation. `message` says which field.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "createDocumentType",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "The category. `restricted: true` means only administrators may upload documents of this type.",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "restricted": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "name"
                ]
              }
            }
          }
        }
      }
    },
    "/userDocuments/types/{documentTypeId}": {
      "get": {
        "tags": [
          "Documents"
        ],
        "summary": "Get a document type",
        "description": "Fetches one document type by id.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/DocumentType"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such document type, or it belongs to another organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getDocumentType",
        "parameters": [
          {
            "name": "documentTypeId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The document type id."
          }
        ]
      },
      "patch": {
        "tags": [
          "Documents"
        ],
        "summary": "Update a document type",
        "description": "Changes the fields you send and leaves the rest alone. Requires the **admin** role.",
        "responses": {
          "200": {
            "description": "The updated document type.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/DocumentType"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The body failed validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "updateDocumentType",
        "parameters": [
          {
            "name": "documentTypeId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The document type id."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Fields to change.",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "restricted": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Documents"
        ],
        "summary": "Delete a document type",
        "description": "Removes the document type. Requires the **admin** role.",
        "responses": {
          "204": {
            "description": "Success. No body."
          },
          "400": {
            "description": "The document type could not be deleted — usually because something still references it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "deleteDocumentType",
        "parameters": [
          {
            "name": "documentTypeId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The document type id."
          }
        ]
      }
    },
    "/groups/orgUser": {
      "get": {
        "tags": [
          "Groups"
        ],
        "summary": "List member groups",
        "description": "Every member group in your organization.",
        "responses": {
          "200": {
            "description": "The member groups.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Group"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "listMemberGroups",
        "parameters": []
      },
      "post": {
        "tags": [
          "Groups"
        ],
        "summary": "Create a member group",
        "description": "Adds a member group to your organization. Requires the **admin** role.",
        "responses": {
          "201": {
            "description": "The member group that was created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Group"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The body failed validation. `message` says which field.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "createMemberGroup",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "The group. The `addNew*` flags auto-enrol future members holding that role.",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "description": {
                    "type": "string"
                  },
                  "orgUserIds": {
                    "type": "array",
                    "items": {
                      "type": "integer"
                    }
                  },
                  "addNewUsers": {
                    "type": "boolean"
                  },
                  "addNewStudents": {
                    "type": "boolean"
                  },
                  "addNewInstructors": {
                    "type": "boolean"
                  },
                  "addNewRenters": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "name"
                ]
              }
            }
          }
        }
      }
    },
    "/groups/orgUser/{orgUserGroupId}": {
      "get": {
        "tags": [
          "Groups"
        ],
        "summary": "Get a member group",
        "description": "Fetches one member group by id.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Group"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such member group, or it belongs to another organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getMemberGroup",
        "parameters": [
          {
            "name": "orgUserGroupId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The member group id."
          }
        ]
      },
      "patch": {
        "tags": [
          "Groups"
        ],
        "summary": "Update a member group",
        "description": "Changes the fields you send and leaves the rest alone. Requires the **admin** role.",
        "responses": {
          "200": {
            "description": "The updated member group.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Group"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The body failed validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "updateMemberGroup",
        "parameters": [
          {
            "name": "orgUserGroupId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The member group id."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Fields to change.",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "description": {
                    "type": "string"
                  },
                  "orgUserIds": {
                    "type": "array",
                    "items": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Groups"
        ],
        "summary": "Delete a member group",
        "description": "Removes the member group. Requires the **admin** role.",
        "responses": {
          "204": {
            "description": "Success. No body."
          },
          "400": {
            "description": "The member group could not be deleted — usually because something still references it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "deleteMemberGroup",
        "parameters": [
          {
            "name": "orgUserGroupId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The member group id."
          }
        ]
      }
    },
    "/groups/resource": {
      "get": {
        "tags": [
          "Groups"
        ],
        "summary": "List resource groups",
        "description": "Every resource group in your organization.",
        "responses": {
          "200": {
            "description": "The resource groups.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Group"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "listResourceGroups",
        "parameters": []
      },
      "post": {
        "tags": [
          "Groups"
        ],
        "summary": "Create a resource group",
        "description": "Adds a resource group to your organization. Requires the **admin** role.",
        "responses": {
          "201": {
            "description": "The resource group that was created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Group"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The body failed validation. `message` says which field.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "createResourceGroup",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "The group.",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "description": {
                    "type": "string"
                  },
                  "resourceIds": {
                    "type": "array",
                    "items": {
                      "type": "integer"
                    }
                  }
                },
                "required": [
                  "name"
                ]
              }
            }
          }
        }
      }
    },
    "/groups/resource/{resourceGroupId}": {
      "get": {
        "tags": [
          "Groups"
        ],
        "summary": "Get a resource group",
        "description": "Fetches one resource group by id.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Group"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such resource group, or it belongs to another organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getResourceGroup",
        "parameters": [
          {
            "name": "resourceGroupId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The resource group id."
          }
        ]
      },
      "patch": {
        "tags": [
          "Groups"
        ],
        "summary": "Update a resource group",
        "description": "Changes the fields you send and leaves the rest alone. Requires the **admin** role.",
        "responses": {
          "200": {
            "description": "The updated resource group.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Group"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The body failed validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "updateResourceGroup",
        "parameters": [
          {
            "name": "resourceGroupId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The resource group id."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Fields to change.",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "resourceIds": {
                    "type": "array",
                    "items": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Groups"
        ],
        "summary": "Delete a resource group",
        "description": "Removes the resource group. Requires the **admin** role.",
        "responses": {
          "204": {
            "description": "Success. No body."
          },
          "400": {
            "description": "The resource group could not be deleted — usually because something still references it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "deleteResourceGroup",
        "parameters": [
          {
            "name": "resourceGroupId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The resource group id."
          }
        ]
      }
    },
    "/maintenance/reminders/templates": {
      "get": {
        "tags": [
          "Maintenance"
        ],
        "summary": "List reminder templates",
        "description": "Every reminder template in your organization.",
        "responses": {
          "200": {
            "description": "The reminder templates.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MaintenanceReminderTemplate"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "listMaintenanceReminderTemplates",
        "parameters": []
      },
      "post": {
        "tags": [
          "Maintenance"
        ],
        "summary": "Create a reminder template",
        "description": "Adds a reminder template to your organization.",
        "responses": {
          "201": {
            "description": "The reminder template that was created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/MaintenanceReminderTemplate"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The body failed validation. `message` says which field.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "createMaintenanceReminderTemplate",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "The rule. Set exactly one of `remindDays`, `remindHours`, or `remindDate`, along with its matching `*Before` field. `remindHours` is an INTERVAL in hours (every 100), not a flag.",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "notes": {
                    "type": "string"
                  },
                  "repeat": {
                    "type": "boolean"
                  },
                  "ground": {
                    "type": "boolean"
                  },
                  "remindHours": {
                    "type": "integer",
                    "example": 100
                  },
                  "remindHoursBefore": {
                    "type": "integer"
                  },
                  "hourBasedOn": {
                    "type": "string",
                    "enum": [
                      "tach",
                      "hobbs"
                    ]
                  },
                  "remindDays": {
                    "type": "integer"
                  },
                  "remindDaysBefore": {
                    "type": "integer"
                  },
                  "remindDate": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "templateResources": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "integer"
                        },
                        "startHour": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                },
                "required": [
                  "name",
                  "repeat"
                ]
              }
            }
          }
        }
      }
    },
    "/maintenance/reminders/templates/{maintenanceReminderTemplateId}": {
      "get": {
        "tags": [
          "Maintenance"
        ],
        "summary": "Get a reminder template",
        "description": "Fetches one reminder template by id.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/MaintenanceReminderTemplate"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such reminder template, or it belongs to another organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getMaintenanceReminderTemplate",
        "parameters": [
          {
            "name": "maintenanceReminderTemplateId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The reminder template id."
          }
        ]
      },
      "patch": {
        "tags": [
          "Maintenance"
        ],
        "summary": "Update a reminder template",
        "description": "Changes the fields you send and leaves the rest alone.",
        "responses": {
          "200": {
            "description": "The updated reminder template.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/MaintenanceReminderTemplate"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The body failed validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "updateMaintenanceReminderTemplate",
        "parameters": [
          {
            "name": "maintenanceReminderTemplateId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The reminder template id."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Fields to change.",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "notes": {
                    "type": "string"
                  },
                  "repeat": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Maintenance"
        ],
        "summary": "Delete a reminder template",
        "description": "Removes the reminder template.",
        "responses": {
          "204": {
            "description": "Success. No body."
          },
          "400": {
            "description": "The reminder template could not be deleted — usually because something still references it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "deleteMaintenanceReminderTemplate",
        "parameters": [
          {
            "name": "maintenanceReminderTemplateId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The reminder template id."
          }
        ]
      }
    },
    "/reports/views": {
      "get": {
        "tags": [
          "Reports"
        ],
        "summary": "List saved report views",
        "description": "Every saved report view in your organization.",
        "responses": {
          "200": {
            "description": "The saved report views.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ReportDefinition"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "listReportDefinitions",
        "parameters": []
      },
      "post": {
        "tags": [
          "Reports"
        ],
        "summary": "Create a saved report view",
        "description": "Adds a saved report view to your organization.",
        "responses": {
          "201": {
            "description": "The saved report view that was created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ReportDefinition"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The body failed validation. `message` says which field.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "createReportDefinition",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "The saved view.",
                "properties": {
                  "reportId": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "config": {
                    "type": "object"
                  },
                  "isShared": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "reportId",
                  "name"
                ]
              }
            }
          }
        }
      }
    },
    "/reports/views/{viewId}": {
      "get": {
        "tags": [
          "Reports"
        ],
        "summary": "Get a saved report view",
        "description": "Fetches one saved report view by id.",
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ReportDefinition"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such saved report view, or it belongs to another organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "getReportDefinition",
        "parameters": [
          {
            "name": "viewId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The saved view id."
          }
        ]
      },
      "patch": {
        "tags": [
          "Reports"
        ],
        "summary": "Update a saved report view",
        "description": "Changes the fields you send and leaves the rest alone.",
        "responses": {
          "200": {
            "description": "The updated saved report view.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ReportDefinition"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The body failed validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "updateReportDefinition",
        "parameters": [
          {
            "name": "viewId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The saved view id."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Fields to change.",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "config": {
                    "type": "object"
                  },
                  "isShared": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Reports"
        ],
        "summary": "Delete a saved report view",
        "description": "Removes the saved report view.",
        "responses": {
          "204": {
            "description": "Success. No body."
          },
          "400": {
            "description": "The saved report view could not be deleted — usually because something still references it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No token, an expired token, or a malformed one. Sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not allowed to do this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `Retry-After` says how long to wait.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "operationId": "deleteReportDefinition",
        "parameters": [
          {
            "name": "viewId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The saved view id."
          }
        ]
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "A bearer token from `POST /auth`, valid for 30 days. Send it as `Authorization: Bearer <token>`.\n\nThe token is scoped to a single organization. To act on another, call `POST /organizations/switch/{orgId}` and use the token it returns."
      }
    },
    "schemas": {
      "Address": {
        "type": "object",
        "description": "A postal address.",
        "properties": {
          "streetAddress1": {
            "type": "string",
            "description": "Street address."
          },
          "streetAddress2": {
            "description": "Suite, unit, or hangar number.",
            "type": [
              "string",
              "null"
            ]
          },
          "city": {
            "type": "string",
            "description": "City."
          },
          "state": {
            "type": "string",
            "description": "State or region."
          },
          "zipCode": {
            "type": "string",
            "description": "Postal code."
          },
          "country": {
            "type": "string",
            "description": "Country."
          },
          "coordinates": {
            "type": "object",
            "description": "Geocoded position, when the address could be resolved.",
            "properties": {
              "lat": {
                "type": "number",
                "description": "Latitude."
              },
              "lng": {
                "type": "number",
                "description": "Longitude."
              }
            },
            "additionalProperties": true
          }
        },
        "additionalProperties": true
      },
      "Organization": {
        "type": "object",
        "description": "A flight school, club, or FBO. Every other record belongs to exactly one.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Unique identifier, stable for the life of the record."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the record was created."
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the record last changed."
          },
          "name": {
            "type": "string",
            "description": "Display name."
          },
          "code": {
            "type": "string",
            "description": "Six-character join code members use to request access.",
            "example": "AERTEST01"
          },
          "organizationType": {
            "description": "Free-text category, e.g. `flight school`.",
            "type": [
              "string",
              "null"
            ]
          },
          "about": {
            "description": "Public description.",
            "type": [
              "string",
              "null"
            ]
          },
          "showInDirectory": {
            "type": "boolean",
            "description": "Whether the organization opts into the public directory."
          },
          "details": {
            "type": "object",
            "description": "Contact details.",
            "properties": {
              "email": {
                "description": "Primary contact email.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "phone": {
                "description": "Primary contact phone.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "address": {
                "type": "object",
                "description": "A postal address.",
                "properties": {
                  "streetAddress1": {
                    "type": "string",
                    "description": "Street address."
                  },
                  "streetAddress2": {
                    "description": "Suite, unit, or hangar number.",
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "city": {
                    "type": "string",
                    "description": "City."
                  },
                  "state": {
                    "type": "string",
                    "description": "State or region."
                  },
                  "zipCode": {
                    "type": "string",
                    "description": "Postal code."
                  },
                  "country": {
                    "type": "string",
                    "description": "Country."
                  },
                  "coordinates": {
                    "type": "object",
                    "description": "Geocoded position, when the address could be resolved.",
                    "properties": {
                      "lat": {
                        "type": "number",
                        "description": "Latitude."
                      },
                      "lng": {
                        "type": "number",
                        "description": "Longitude."
                      }
                    },
                    "additionalProperties": true
                  }
                },
                "additionalProperties": true
              }
            },
            "additionalProperties": true
          },
          "preferences": {
            "type": "object",
            "description": "Per-organization behaviour switches.",
            "properties": {},
            "additionalProperties": true
          },
          "locations": {
            "type": "array",
            "description": "Bases this organization operates from.",
            "items": {
              "$ref": "#/components/schemas/Location"
            }
          }
        },
        "additionalProperties": true
      },
      "Location": {
        "type": "object",
        "description": "A base of operations — a field, hangar, or classroom building.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Unique identifier, stable for the life of the record."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the record was created."
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the record last changed."
          },
          "name": {
            "type": "string",
            "description": "Display name.",
            "example": "Main Hangar"
          },
          "showInDirectory": {
            "type": "boolean",
            "description": "Whether this location appears in the public directory."
          },
          "timeZone": {
            "description": "IANA time zone of the field, e.g. `America/Denver`. Scheduling is anchored to the AIRPORT's zone, not the caller's device — see the Time zones section.",
            "example": "America/Denver",
            "type": [
              "string",
              "null"
            ]
          },
          "address": {
            "type": "object",
            "description": "A postal address.",
            "properties": {
              "streetAddress1": {
                "type": "string",
                "description": "Street address."
              },
              "streetAddress2": {
                "description": "Suite, unit, or hangar number.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "city": {
                "type": "string",
                "description": "City."
              },
              "state": {
                "type": "string",
                "description": "State or region."
              },
              "zipCode": {
                "type": "string",
                "description": "Postal code."
              },
              "country": {
                "type": "string",
                "description": "Country."
              },
              "coordinates": {
                "type": "object",
                "description": "Geocoded position, when the address could be resolved.",
                "properties": {
                  "lat": {
                    "type": "number",
                    "description": "Latitude."
                  },
                  "lng": {
                    "type": "number",
                    "description": "Longitude."
                  }
                },
                "additionalProperties": true
              }
            },
            "additionalProperties": true
          }
        },
        "additionalProperties": true
      },
      "Plane": {
        "type": "object",
        "description": "Aircraft-specific detail on a resource whose type is a plane.",
        "properties": {
          "tailNumber": {
            "type": "string",
            "description": "Registration, e.g. `N12345`.",
            "example": "N12345"
          },
          "make": {
            "type": "string",
            "description": "Manufacturer.",
            "example": "Cessna"
          },
          "model": {
            "type": "string",
            "description": "Model designation.",
            "example": "172S"
          },
          "year": {
            "description": "Model year, four digits.",
            "example": "2019",
            "type": [
              "string",
              "null"
            ]
          },
          "categoryClass": {
            "type": "string",
            "description": "One of `single-engine land`, `multi-engine land`, `single-engine sea`, `multi-engine sea`.",
            "enum": [
              "single-engine land",
              "multi-engine land",
              "single-engine sea",
              "multi-engine sea"
            ]
          },
          "tachTime": {
            "type": "integer",
            "description": "Current tach reading."
          },
          "hobbsTime": {
            "type": "integer",
            "description": "Current Hobbs reading."
          },
          "rampedIn": {
            "type": "boolean",
            "description": "True when the aircraft is on the ramp and available."
          },
          "grounded": {
            "type": "boolean",
            "description": "True when the aircraft is unavailable for booking."
          },
          "groundedReason": {
            "description": "Why it is grounded.",
            "type": [
              "string",
              "null"
            ]
          },
          "fuelCapacity": {
            "type": "integer",
            "description": "Usable fuel capacity."
          },
          "fuelMeasurement": {
            "type": "string",
            "description": "Units for fuel figures.",
            "enum": [
              "gallons",
              "liters"
            ]
          },
          "fuelEngine1": {
            "type": "integer",
            "description": "Fuel on board, engine 1."
          },
          "fuelEngine2": {
            "description": "Fuel on board, engine 2, for twins.",
            "type": [
              "integer",
              "null"
            ]
          },
          "cost": {
            "type": "object",
            "description": "Billing rates, in cents.",
            "properties": {
              "dryRate": {
                "description": "Dry rate per hour, in cents.",
                "example": 16500,
                "type": [
                  "integer",
                  "null"
                ]
              },
              "wetRate": {
                "description": "Wet rate per hour, in cents.",
                "example": 19500,
                "type": [
                  "integer",
                  "null"
                ]
              },
              "billByHobbsTime": {
                "type": "boolean",
                "description": "Bill by Hobbs (true) or tach (false)."
              }
            },
            "additionalProperties": true
          }
        },
        "additionalProperties": true
      },
      "Resource": {
        "type": "object",
        "description": "Anything that can be booked: an aircraft, a simulator, or a room. The `type` object carries whichever detail applies.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Unique identifier, stable for the life of the record."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the record was created."
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the record last changed."
          },
          "name": {
            "type": "string",
            "description": "Display name.",
            "example": "N12345"
          },
          "featuredImage": {
            "description": "URL of the resource photo.",
            "type": [
              "string",
              "null"
            ]
          },
          "location": {
            "$ref": "#/components/schemas/Location"
          },
          "type": {
            "type": "object",
            "description": "Exactly one of these is populated.",
            "properties": {
              "plane": {
                "$ref": "#/components/schemas/Plane"
              },
              "simulator": {
                "type": "object",
                "description": "Simulator detail.",
                "properties": {},
                "additionalProperties": true
              },
              "room": {
                "type": "object",
                "description": "Room detail.",
                "properties": {
                  "roomNumber": {
                    "type": "string",
                    "description": "Room number."
                  }
                },
                "additionalProperties": true
              }
            },
            "additionalProperties": true
          }
        },
        "additionalProperties": true
      },
      "User": {
        "type": "object",
        "description": "A person. One account can belong to many organizations.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Unique identifier, stable for the life of the record."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the record was created."
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the record last changed."
          },
          "name": {
            "type": "string",
            "description": "Full name."
          },
          "email": {
            "type": "string",
            "description": "Email address. Unique across the system.",
            "format": "email"
          },
          "phone": {
            "description": "Phone number.",
            "type": [
              "string",
              "null"
            ]
          },
          "profileImage": {
            "description": "URL of the profile photo.",
            "type": [
              "string",
              "null"
            ]
          },
          "emailVerifiedAt": {
            "format": "date-time",
            "description": "When the address was verified. Null means unverified.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "additionalProperties": true
      },
      "OrganizationUser": {
        "type": "object",
        "description": "A person's membership of an organization. This — not `User` — is what schedules, invoices, and permissions hang off, because one person can belong to several organizations with different roles in each.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Unique identifier, stable for the life of the record."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the record was created."
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the record last changed."
          },
          "user": {
            "$ref": "#/components/schemas/User"
          },
          "roles": {
            "type": "array",
            "description": "Roles held in this organization. Additive and non-hierarchical: a person can be both instructor and technician.",
            "items": {
              "type": "string",
              "enum": [
                "owner",
                "admin",
                "dispatcher",
                "instructor",
                "student",
                "renter",
                "technician"
              ]
            }
          }
        },
        "additionalProperties": true
      },
      "Reservation": {
        "type": "object",
        "description": "A booking of a resource, a person, or both.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Unique identifier, stable for the life of the record."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the record was created."
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the record last changed."
          },
          "title": {
            "type": "string",
            "description": "Short label shown on the calendar.",
            "maxLength": 50
          },
          "type": {
            "type": "string",
            "description": "What kind of booking this is. Which types a caller may CREATE depends on their roles — see the Reservation types section.",
            "enum": [
              "solo",
              "dual",
              "ground",
              "guest",
              "sim",
              "rental",
              "maintenance",
              "other"
            ]
          },
          "start": {
            "type": "string",
            "format": "date-time",
            "description": "Start instant. MUST carry an explicit offset or `Z`."
          },
          "end": {
            "type": "string",
            "format": "date-time",
            "description": "End instant. MUST carry an explicit offset or `Z`."
          },
          "timeZoneName": {
            "type": "string",
            "description": "IANA zone the booking was made in, e.g. `America/Denver`.",
            "example": "America/Denver"
          },
          "notes": {
            "description": "Free-text notes.",
            "maxLength": 200,
            "type": [
              "string",
              "null"
            ]
          },
          "cancelledAt": {
            "format": "date-time",
            "description": "When it was cancelled. Null for a live booking.",
            "type": [
              "string",
              "null"
            ]
          },
          "cancellationReason": {
            "description": "Free-text reason given at cancellation.",
            "type": [
              "string",
              "null"
            ]
          },
          "cancellationCategory": {
            "description": "Fixed-list reason. Null on bookings cancelled before this was introduced — reports count those as “Not recorded”, not “Other”.",
            "type": [
              "string",
              "null"
            ]
          },
          "rrule": {
            "description": "Recurrence rule, when this booking belongs to a repeating series.",
            "type": [
              "string",
              "null"
            ]
          },
          "resource": {
            "$ref": "#/components/schemas/Resource"
          },
          "location": {
            "$ref": "#/components/schemas/Location"
          },
          "personnel": {
            "type": "object",
            "description": "Who is on the booking.",
            "properties": {
              "instructors": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/OrganizationUser"
                }
              },
              "students": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/OrganizationUser"
                }
              },
              "renters": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/OrganizationUser"
                }
              },
              "guests": {
                "type": "array",
                "items": {
                  "type": "object",
                  "description": "A non-member on the booking.",
                  "properties": {
                    "name": {
                      "type": "string",
                      "description": "Name."
                    },
                    "email": {
                      "type": "string",
                      "description": "Email."
                    },
                    "phone": {
                      "type": "string",
                      "description": "Phone."
                    }
                  },
                  "additionalProperties": true
                }
              }
            },
            "additionalProperties": true
          },
          "review": {
            "type": "object",
            "description": "Close-out figures, populated once the flight is ramped out and back in.",
            "properties": {
              "hobbsTimeOut": {
                "description": "Hobbs at departure.",
                "type": [
                  "number",
                  "null"
                ]
              },
              "hobbsTimeIn": {
                "description": "Hobbs at return.",
                "type": [
                  "number",
                  "null"
                ]
              },
              "tachTimeOut": {
                "description": "Tach at departure.",
                "type": [
                  "number",
                  "null"
                ]
              },
              "tachTimeIn": {
                "description": "Tach at return.",
                "type": [
                  "number",
                  "null"
                ]
              }
            },
            "additionalProperties": true
          }
        },
        "additionalProperties": true
      },
      "Squawk": {
        "type": "object",
        "description": "A reported aircraft discrepancy.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Unique identifier, stable for the life of the record."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the record was created."
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the record last changed."
          },
          "title": {
            "type": "string",
            "description": "Short summary.",
            "example": "Nav light flickering"
          },
          "description": {
            "type": "string",
            "description": "Full description of the discrepancy."
          },
          "reportedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When it was reported."
          },
          "resolvedAt": {
            "format": "date-time",
            "description": "When it was cleared. Null while open.",
            "type": [
              "string",
              "null"
            ]
          },
          "resource": {
            "$ref": "#/components/schemas/Resource"
          }
        },
        "additionalProperties": true
      },
      "MaintenanceReminder": {
        "type": "object",
        "description": "A due-date or due-hours reminder against one aircraft.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Unique identifier, stable for the life of the record."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the record was created."
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the record last changed."
          },
          "startedAt": {
            "format": "date-time",
            "description": "When the current interval began.",
            "type": [
              "string",
              "null"
            ]
          },
          "startHours": {
            "description": "Meter reading the interval started at.",
            "type": [
              "integer",
              "null"
            ]
          },
          "completedAt": {
            "format": "date-time",
            "description": "When the work was signed off.",
            "type": [
              "string",
              "null"
            ]
          },
          "resource": {
            "$ref": "#/components/schemas/Resource"
          }
        },
        "additionalProperties": true
      },
      "MaintenanceReminderTemplate": {
        "type": "object",
        "description": "The rule a maintenance reminder repeats on.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Unique identifier, stable for the life of the record."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the record was created."
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the record last changed."
          },
          "name": {
            "type": "string",
            "description": "Display name.",
            "example": "100 Hour Inspection"
          },
          "notes": {
            "description": "Free-text notes.",
            "type": [
              "string",
              "null"
            ]
          },
          "repeat": {
            "type": "boolean",
            "description": "Whether a new interval starts when this one is signed off."
          },
          "ground": {
            "type": "boolean",
            "description": "Whether the aircraft is grounded when this comes due."
          },
          "remindDays": {
            "description": "Interval in days, for date-based reminders.",
            "type": [
              "integer",
              "null"
            ]
          },
          "remindDaysBefore": {
            "description": "How many days ahead to warn.",
            "type": [
              "integer",
              "null"
            ]
          },
          "remindHours": {
            "description": "Interval in hours, for meter-based reminders.",
            "example": 100,
            "type": [
              "integer",
              "null"
            ]
          },
          "remindHoursBefore": {
            "description": "How many hours ahead to warn.",
            "type": [
              "integer",
              "null"
            ]
          },
          "hourBasedOn": {
            "description": "Which meter drives it.",
            "enum": [
              "tach",
              "hobbs"
            ],
            "type": [
              "string",
              "null"
            ]
          },
          "remindDate": {
            "format": "date-time",
            "description": "A one-off due date.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "additionalProperties": true
      },
      "Invoice": {
        "type": "object",
        "description": "A bill for a completed reservation.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Unique identifier, stable for the life of the record."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the record was created."
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the record last changed."
          },
          "paidAt": {
            "format": "date-time",
            "description": "When it was paid. Null while outstanding.",
            "type": [
              "string",
              "null"
            ]
          },
          "dueAt": {
            "format": "date-time",
            "description": "When payment is due.",
            "type": [
              "string",
              "null"
            ]
          },
          "total": {
            "description": "Total in cents.",
            "type": [
              "integer",
              "null"
            ]
          },
          "items": {
            "type": "array",
            "description": "Line items.",
            "items": {
              "type": "object",
              "description": "A line item.",
              "properties": {
                "description": {
                  "type": "string",
                  "description": "What it is for."
                },
                "amount": {
                  "type": "integer",
                  "description": "Amount in cents."
                }
              },
              "additionalProperties": true
            }
          },
          "orgUser": {
            "$ref": "#/components/schemas/OrganizationUser"
          },
          "reservation": {
            "$ref": "#/components/schemas/Reservation"
          }
        },
        "additionalProperties": true
      },
      "Announcement": {
        "type": "object",
        "description": "A notice shown to members.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Unique identifier, stable for the life of the record."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the record was created."
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the record last changed."
          },
          "title": {
            "type": "string",
            "description": "Headline."
          },
          "message": {
            "type": "string",
            "description": "Body text."
          },
          "expireAt": {
            "format": "date-time",
            "description": "When it stops being shown. Expired notices are swept hourly.",
            "type": [
              "string",
              "null"
            ]
          },
          "forRoles": {
            "type": "array",
            "description": "Roles that should see it. Empty means everyone.",
            "items": {
              "type": "string"
            }
          }
        },
        "additionalProperties": true
      },
      "Notification": {
        "type": "object",
        "description": "An in-app notification for one member.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Unique identifier, stable for the life of the record."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the record was created."
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the record last changed."
          },
          "title": {
            "type": "string",
            "description": "Headline."
          },
          "subtitle": {
            "description": "Secondary line.",
            "type": [
              "string",
              "null"
            ]
          },
          "link": {
            "description": "Deep link into the app or console.",
            "type": [
              "string",
              "null"
            ]
          },
          "readAt": {
            "format": "date-time",
            "description": "When it was read. Null while unread.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "additionalProperties": true
      },
      "CurrencyType": {
        "type": "object",
        "description": "A recurring requirement members must stay current on — a flight review, a medical, a checkout.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Unique identifier, stable for the life of the record."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the record was created."
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the record last changed."
          },
          "name": {
            "type": "string",
            "description": "Display name.",
            "example": "Flight Review"
          },
          "description": {
            "description": "What it covers.",
            "type": [
              "string",
              "null"
            ]
          },
          "active": {
            "type": "boolean",
            "description": "Whether it is being tracked."
          },
          "expiresInDays": {
            "description": "Validity in days.",
            "type": [
              "integer",
              "null"
            ]
          },
          "expiresInMonths": {
            "description": "Validity in months.",
            "example": 24,
            "type": [
              "integer",
              "null"
            ]
          },
          "expiresOn": {
            "format": "date-time",
            "description": "A fixed expiry date, for one-off requirements.",
            "type": [
              "string",
              "null"
            ]
          },
          "warningPeriodInDays": {
            "description": "How long before expiry to start warning.",
            "type": [
              "integer",
              "null"
            ]
          },
          "canRenewSelf": {
            "type": "boolean",
            "description": "Whether a member may sign off their own renewal."
          },
          "instructorCanRenew": {
            "type": "boolean",
            "description": "Whether an instructor may sign it off."
          },
          "dispatcherCanRenew": {
            "type": "boolean",
            "description": "Whether a dispatcher may sign it off."
          }
        },
        "additionalProperties": true
      },
      "Currency": {
        "type": "object",
        "description": "One member's standing against one currency type. Created automatically when a type is attached to a member group — there is no endpoint that creates one directly.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Unique identifier, stable for the life of the record."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the record was created."
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the record last changed."
          },
          "startedAt": {
            "format": "date-time",
            "description": "When the current period began.",
            "type": [
              "string",
              "null"
            ]
          },
          "expiresAt": {
            "format": "date-time",
            "description": "When it lapses.",
            "type": [
              "string",
              "null"
            ]
          },
          "orgUser": {
            "$ref": "#/components/schemas/OrganizationUser"
          },
          "currencyType": {
            "$ref": "#/components/schemas/CurrencyType"
          }
        },
        "additionalProperties": true
      },
      "DocumentType": {
        "type": "object",
        "description": "A category of member document — medical, photo ID, endorsement.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Unique identifier, stable for the life of the record."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the record was created."
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the record last changed."
          },
          "name": {
            "type": "string",
            "description": "Display name.",
            "example": "Medical Certificate"
          },
          "restricted": {
            "type": "boolean",
            "description": "When true, only administrators may upload documents of this type on a member's behalf."
          }
        },
        "additionalProperties": true
      },
      "UserDocument": {
        "type": "object",
        "description": "A file held against a member.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Unique identifier, stable for the life of the record."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the record was created."
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the record last changed."
          },
          "name": {
            "type": "string",
            "description": "File name."
          },
          "url": {
            "description": "Location of the file. Fetch through the signed-URL endpoint rather than linking directly.",
            "type": [
              "string",
              "null"
            ]
          },
          "expiresAt": {
            "format": "date-time",
            "description": "When the document lapses.",
            "type": [
              "string",
              "null"
            ]
          },
          "documentType": {
            "$ref": "#/components/schemas/DocumentType"
          },
          "orgUser": {
            "$ref": "#/components/schemas/OrganizationUser"
          }
        },
        "additionalProperties": true
      },
      "Group": {
        "type": "object",
        "description": "A named set of members or resources, used to scope requirements and permissions in bulk.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Unique identifier, stable for the life of the record."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the record was created."
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the record last changed."
          },
          "name": {
            "type": "string",
            "description": "Display name."
          },
          "description": {
            "description": "What the group is for.",
            "type": [
              "string",
              "null"
            ]
          },
          "addNewUsers": {
            "type": "boolean",
            "description": "Automatically add every new member."
          }
        },
        "additionalProperties": true
      },
      "Availability": {
        "type": "object",
        "description": "A window in which a person or resource is free.",
        "properties": {
          "start": {
            "type": "string",
            "format": "date-time",
            "description": "Window start."
          },
          "end": {
            "type": "string",
            "format": "date-time",
            "description": "Window end."
          }
        },
        "additionalProperties": true
      },
      "ReportDefinition": {
        "type": "object",
        "description": "One report in the catalog.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable identifier to pass to `POST /reports/run`.",
            "example": "revenue"
          },
          "name": {
            "type": "string",
            "description": "Display name."
          },
          "description": {
            "type": "string",
            "description": "What the report answers."
          },
          "category": {
            "type": "string",
            "description": "Grouping key.",
            "enum": [
              "financial",
              "operations",
              "fleet",
              "people",
              "compliance"
            ]
          }
        },
        "additionalProperties": true
      },
      "ReportResult": {
        "type": "object",
        "description": "The output of a report run.",
        "properties": {
          "columns": {
            "type": "array",
            "description": "Column definitions, in display order.",
            "items": {
              "type": "object",
              "description": "A column.",
              "properties": {
                "key": {
                  "type": "string",
                  "description": "Field key."
                },
                "label": {
                  "type": "string",
                  "description": "Header."
                },
                "unit": {
                  "description": "Unit, where the value is not a plain number.",
                  "type": [
                    "string",
                    "null"
                  ]
                }
              },
              "additionalProperties": true
            }
          },
          "rows": {
            "type": "array",
            "description": "Result rows, keyed by column key.",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "totals": {
            "type": "object",
            "description": "Column totals, where the column is summable.",
            "properties": {},
            "additionalProperties": true
          }
        },
        "additionalProperties": true
      },
      "SearchResults": {
        "type": "object",
        "description": "Matches across every entity type the caller may see.",
        "properties": {
          "resources": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Resource"
            }
          },
          "orgUsers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrganizationUser"
            }
          },
          "reservations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Reservation"
            }
          },
          "locations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Location"
            }
          }
        },
        "additionalProperties": true
      },
      "AuthEnvelope": {
        "type": "object",
        "description": "Returned by every endpoint that establishes a session.",
        "properties": {
          "auth": {
            "type": "object",
            "description": "The credential to send on subsequent requests.",
            "properties": {
              "accessToken": {
                "type": "string",
                "description": "Bearer token. Valid for 30 days."
              }
            },
            "additionalProperties": true
          },
          "data": {
            "type": "object",
            "description": "Who you are and where.",
            "properties": {
              "user": {
                "$ref": "#/components/schemas/User"
              },
              "organization": {
                "$ref": "#/components/schemas/Organization"
              },
              "organizations": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Organization"
                }
              }
            },
            "additionalProperties": true
          }
        },
        "additionalProperties": true
      },
      "Error": {
        "type": "object",
        "description": "Every error response has this shape.",
        "properties": {
          "message": {
            "type": "string",
            "description": "Human-readable explanation, safe to show a user."
          },
          "retryAfter": {
            "type": "integer",
            "description": "Seconds to wait before retrying. Present on 429 only."
          }
        },
        "required": [
          "message"
        ]
      }
    }
  }
}